Firstly, I'm sorry if this has been posted before. I searched but couldn't find any credible solution.
So I'm working on this route in nodejs where I make an API call for a piece of information and then using that info in an if statement to check if it's the correct info(the server sometimes sends wrong info).
If I get the correct info then I use that in another API to get more info about it and render it into my template. Everything works fine.
But I want the first API call to take place again if the info doesn't match or it's wrong. How can I initiate the API call again from the start(like a loop) and it will break only if the info is correct. Please check the "comment" in the code below. That is where I don't know what to put. Your help would be highly appreciated.
PS. I am a beginner in nodejs and javascript.
Route
router.get("/check", (req, res) => {
if(req.query.search) {
var input = req.query.search;
var url = "http://firstapi.com/json/" + input + "?fields=query";
request(url, function(error, response, body) {
if(!error && response.statusCode === 200) {
var data = JSON.parse(body);
if(data.query.match(/((^|\.)((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]?\d))){4}$/)){
var url = "https://secondapi.com/" + data.query + "?key=something";
request(url, function(error, response, body) {
if(!error && response.statusCode === 200) {
var Data = JSON.parse(body);
res.render("index", {data: Data});
}
});
}else{
//want to use the input at the top and check the firstapi again. All the code above should run again until its the correct one which I will use in my template.
}
}
});
}else{
res.render("index", {data: null});
}
});
I would probably do it this way:
router.get('/check', (req, res) => {
if (req.query.search) {
var input = req.query.search;
// Put this logic away in a `checkData` function
checkData(input)
.then(data => {
res.render('index', { data }); // Short version of {data: data}
})
.catch(err => {
console.error(err);
res.render('index', { data: null });
});
} else {
res.render('index', { data: null });
}
});
// Here, we have a `retries` parameter, set to 0 initially
function checkData (input, retries = 0) {
const maxRetries = 3;
// Return a promise (you could also use callbacks)
return new Promise((resolve, reject) => {
// Define a retry method
const retry = () => {
if (retries < maxRetries) {
// Increment the retries count and go for another try
checkData(input, retries + 1).then(resolve).catch(reject);
} else {
reject(`Could not get the data after ${retries} retries.`);
}
};
var url = `http://firstapi.com/json/${input}?fields=query`;
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = JSON.parse(body);
if (data.query.match(/((^|\.)((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]?\d))){4}$/)) {
var url = 'https://secondapi.com/' + data.query + '?key=something';
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var Data = JSON.parse(body);
// If it worked, resolve with the data
resolve(Data);
} else {
retry();
}
});
} else {
retry();
}
}
});
});
}
I want to fire an http API and process it's response but I am getting following error:
Exception while executing function: Functions.getAccessObject. mscorlib: ReferenceError: XMLHttpRequest is not defined
Here's my Azure serverless function Code:
module.exports = function (context, req) {
function getAccessObject(context, successCallback, failureCallback) {
var APPLICATION_ID = "zzz";
var APPLICATION_SECRET = "zzz";
var REFRESH_TOKEN = "zzz";
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET", "https://xyz");
xhr.setRequestHeader("applicationid", APPLICATION_ID);
xhr.setRequestHeader("applicationsecret", APPLICATION_SECRET);
xhr.setRequestHeader("refreshtoken", REFRESH_TOKEN);
xhr.setRequestHeader("cache-control", "no-cache");
xhr.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
successCallback(context, request.responseText);
} else {
failureCallback(context, request.responseText);
}
}
};
request.send(null);
}
getAccessObject(context, registerForWebhookFunc, failureCallbackFunc);
}
I believe XHR is a browser API and not native to node.js. You can use the built-in http functionality of node.js (see example below) or alternatively other popular packages are available such as Axios that can do http requests. I believe there may even be one for XHR if you are set on using that.
const https = require('https');
https.get('http://myapi', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log(JSON.parse(data));
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
Here is my service call
<script>
function secureClicked() {
var uname = document.getElementById('uname').value;
var pwd = document.getElementById('pwd').value;
var obj = new Object();
obj.username = uname;
obj.password = pwd;
var userObj = JSON.stringify(obj);
xhr = new XMLHttpRequest();
var url = "http://127.0.0.1:9923/login";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
var json = JSON.parse(xhr.responseText);
document.write(json.accesstoken);
onComplete(xmlhttp.responseText);
}
};
xhr.send(userObj);
}
Here is my backend code
module.exports = {
login: function(req, res) {
MongoClient.connect(mongoUrl, function (err, db) {
if (err) {
console.log('into error of mongo');
res.status(400).json({
message: 'Connection to database failed !!',
error: err
});
}
insertDocument(db, req.body, function (result) {
console.log('into insert doc');
res.setHeader('Content-Type', 'application/json');
res.status(200).json({
access_token: result._id
});
});
});
var insertDocument = function (db, data, callback) {
console.log('into insert doc function');
console.log(data);
var string = JSON.stringify(data);
var objectValue = JSON.parse(string);
var username = objectValue['username'];
var password = objectValue['password'];
console.log(username);
db.collection('user_list').findOne({$and:[{ 'username':username},{'password':password}]}, function(err, result) {
if (err) {
res.status(500).json({
message: 'Failed to add in DB!!'
});
} else {
callback(result);
}
});
}
}
};
I'm able to get response in postman, but unable to get response in browser.
Am I doing anything worng here ?
Can anyone help me on this?
Thanks in advance !!
I want to open a page up in node and process the contents in my application. Something like this seems to work well:
var opts = {host: host, path:pathname, port: 80};
http.get(opts, function(res) {
var page = '';
res.on('data', function (chunk) {
page += chunk;
});
res.on('end', function() {
// process page
});
This doesn't work, however, if the page returns an 301/302 redirect. How would I do that in a reusable way in case there are multiple redirects? Is there a wrapper module on top of the http to more easily handle processing http responses from a node application?
If all you want to do is follow redirects but still want to use the built-in HTTP and HTTPS modules, I suggest you use https://github.com/follow-redirects/follow-redirects.
yarn add follow-redirects
npm install follow-redirects
All you need to do is replace:
var http = require('http');
with
var http = require('follow-redirects').http;
... and all your requests will automatically follow redirects.
With TypeScript you can also install the types
npm install #types/follow-redirects
and then use
import { http, https } from 'follow-redirects';
Disclosure: I wrote this module.
Is there a wrapper module on top of the http to more easily handle processing http responses from a node application?
request
Redirection logic in request
Make another request based on response.headers.location:
const request = function(url) {
lib.get(url, (response) => {
var body = [];
if (response.statusCode == 302) {
body = [];
request(response.headers.location);
} else {
response.on("data", /*...*/);
response.on("end", /*...*/);
};
} ).on("error", /*...*/);
};
request(url);
Update:
Now you can follow all redirects with var request = require('request'); using the followAllRedirects param.
request({
followAllRedirects: true,
url: url
}, function (error, response, body) {
if (!error) {
console.log(response);
}
});
Here is my (recursive) approach to download JSON with plain node, no packages required.
import https from "https";
function get(url, resolve, reject) {
https.get(url, (res) => {
// if any other status codes are returned, those needed to be added here
if(res.statusCode === 301 || res.statusCode === 302) {
return get(res.headers.location, resolve, reject)
}
let body = [];
res.on("data", (chunk) => {
body.push(chunk);
});
res.on("end", () => {
try {
// remove JSON.parse(...) for plain data
resolve(JSON.parse(Buffer.concat(body).toString()));
} catch (err) {
reject(err);
}
});
});
}
async function getData(url) {
return new Promise((resolve, reject) => get(url, resolve, reject));
}
// call
getData("some-url-with-redirect").then((r) => console.log(r));
Here is function I use to fetch the url that have redirect:
const http = require('http');
const url = require('url');
function get({path, host}, callback) {
http.get({
path,
host
}, function(response) {
if (response.headers.location) {
var loc = response.headers.location;
if (loc.match(/^http/)) {
loc = new Url(loc);
host = loc.host;
path = loc.path;
} else {
path = loc;
}
get({host, path}, callback);
} else {
callback(response);
}
});
}
it work the same as http.get but follow redirect.
In case of PUT or POST Request. if you receive statusCode 405 or method not allowed. Try this implementation with "request" library, and add mentioned properties.
followAllRedirects: true,
followOriginalHttpMethod: true
const options = {
headers: {
Authorization: TOKEN,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
url: `https://${url}`,
json: true,
body: payload,
followAllRedirects: true,
followOriginalHttpMethod: true
}
console.log('DEBUG: API call', JSON.stringify(options));
request(options, function (error, response, body) {
if (!error) {
console.log(response);
}
});
}
If you have https server, change your url to use https:// protocol.
I got into similar issue with this one. My url has http:// protocol and I want to make a POST request, but the server wants to redirect it to https. What happen is that, turns out to be node http behavior sends the redirect request (next) in GET method which is not the case.
What I did is to change my url to https:// protocol and it works.
Possibly a little bit of a necromancing post here, but...
here's a function that follows up to 10 redirects, and detects infinite redirect loops. also parses result into JSON
Note - uses a callback helper (shown at the end of this post)
( TLDR; full working demo in context here or remixed-version here)
function getJSON(url,cb){
var callback=errBack(cb);
//var callback=errBack(cb,undefined,false);//replace previous line with this to turn off logging
if (typeof url!=='string') {
return callback.error("getJSON:expecting url as string");
}
if (typeof cb!=='function') {
return callback.error("getJSON:expecting cb as function");
}
var redirs = [url],
fetch = function(u){
callback.info("hitting:"+u);
https.get(u, function(res){
var body = [];
callback.info({statusCode:res.statusCode});
if ([301,302].indexOf(res.statusCode)>=0) {
if (redirs.length>10) {
return callback.error("excessive 301/302 redirects detected");
} else {
if (redirs.indexOf(res.headers.location)<0) {
redirs.push(res.headers.location);
return fetch(res.headers.location);
} else {
return callback.error("301/302 redirect loop detected");
}
}
} else {
res.on('data', function(chunk){
body.push(chunk);
callback.info({onData:{chunkSize:chunk.length,chunks:body.length}});
});
res.on('end', function(){
try {
// convert to a single buffer
var json = Buffer.concat(body);
console.info({onEnd:{chunks:body.length,bodyLength:body.length}});
// parse the buffer as json
return callback.result(JSON.parse(json),json);
} catch (err) {
console.error("exception in getJSON.fetch:",err.message||err);
if (json.length>32) {
console.error("json==>|"+json.toString('utf-8').substr(0,32)+"|<=== ... (+"+(json.length-32)+" more bytes of json)");
} else {
console.error("json==>|"+json.toString('utf-8')+"|<=== json");
}
return callback.error(err,undefined,json);
}
});
}
});
};
fetch(url);
}
Note - uses a callback helper (shown below)
you can paste this into the node console and it should run as is.
( or for full working demo in context see here )
var
fs = require('fs'),
https = require('https');
function errBack (cb,THIS,logger) {
var
self,
EB=function(fn,r,e){
if (logger===false) {
fn.log=fn.info=fn.warn=fn.errlog=function(){};
} else {
fn.log = logger?logger.log : console.log.bind(console);
fn.info = logger?logger.info : console.info.bind(console);
fn.warn = logger?logger.warn : console.warn.bind(console);
fn.errlog = logger?logger.error : console.error.bind(console);
}
fn.result=r;
fn.error=e;
return (self=fn);
};
if (typeof cb==='function') {
return EB(
logger===false // optimization when not logging - don't log errors
? function(err){
if (err) {
cb (err);
return true;
}
return false;
}
: function(err){
if (err) {
self.errlog(err);
cb (err);
return true;
}
return false;
},
function () {
return cb.apply (THIS,Array.prototype.concat.apply([undefined],arguments));
},
function (err) {
return cb.apply (THIS,Array.prototype.concat.apply([typeof err==='string'?new Error(err):err],arguments));
}
);
} else {
return EB(
function(err){
if (err) {
if (typeof err ==='object' && err instanceof Error) {
throw err;
} else {
throw new Error(err);
}
return true;//redundant due to throw, but anyway.
}
return false;
},
logger===false
? self.log //optimization :resolves to noop when logger==false
: function () {
self.info("ignoring returned arguments:",Array.prototype.concat.apply([],arguments));
},
function (err) {
throw typeof err==='string'?new Error(err):err;
}
);
}
}
function getJSON(url,cb){
var callback=errBack(cb);
if (typeof url!=='string') {
return callback.error("getJSON:expecting url as string");
}
if (typeof cb!=='function') {
return callback.error("getJSON:expecting cb as function");
}
var redirs = [url],
fetch = function(u){
callback.info("hitting:"+u);
https.get(u, function(res){
var body = [];
callback.info({statusCode:res.statusCode});
if ([301,302].indexOf(res.statusCode)>=0) {
if (redirs.length>10) {
return callback.error("excessive 302 redirects detected");
} else {
if (redirs.indexOf(res.headers.location)<0) {
redirs.push(res.headers.location);
return fetch(res.headers.location);
} else {
return callback.error("302 redirect loop detected");
}
}
} else {
res.on('data', function(chunk){
body.push(chunk);
console.info({onData:{chunkSize:chunk.length,chunks:body.length}});
});
res.on('end', function(){
try {
// convert to a single buffer
var json = Buffer.concat(body);
callback.info({onEnd:{chunks:body.length,bodyLength:body.length}});
// parse the buffer as json
return callback.result(JSON.parse(json),json);
} catch (err) {
// read with "bypass refetch" option
console.error("exception in getJSON.fetch:",err.message||err);
if (json.length>32) {
console.error("json==>|"+json.toString('utf-8').substr(0,32)+"|<=== ... (+"+(json.length-32)+" more bytes of json)");
} else {
console.error("json==>|"+json.toString('utf-8')+"|<=== json");
}
return callback.error(err,undefined,json);
}
});
}
});
};
fetch(url);
}
var TLDs,TLDs_fallback = "com.org.tech.net.biz.info.code.ac.ad.ae.af.ag.ai.al.am.ao.aq.ar.as.at.au.aw.ax.az.ba.bb.bd.be.bf.bg.bh.bi.bj.bm.bn.bo.br.bs.bt.bv.bw.by.bz.ca.cc.cd.cf.cg.ch.ci.ck.cl.cm.cn.co.cr.cu.cv.cw.cx.cy.cz.de.dj.dk.dm.do.dz.ec.ee.eg.er.es.et.eu.fi.fj.fk.fm.fo.fr.ga.gb.gd.ge.gf.gg.gh.gi.gl.gm.gn.gp.gq.gr.gs.gt.gu.gw.gy.hk.hm.hn.hr.ht.hu.id.ie.il.im.in.io.iq.ir.is.it.je.jm.jo.jp.ke.kg.kh.ki.km.kn.kp.kr.kw.ky.kz.la.lb.lc.li.lk.lr.ls.lt.lu.lv.ly.ma.mc.md.me.mg.mh.mk.ml.mm.mn.mo.mp.mq.mr.ms.mt.mu.mv.mw.mx.my.mz.na.nc.ne.nf.ng.ni.nl.no.np.nr.nu.nz.om.pa.pe.pf.pg.ph.pk.pl.pm.pn.pr.ps.pt.pw.py.qa.re.ro.rs.ru.rw.sa.sb.sc.sd.se.sg.sh.si.sj.sk.sl.sm.sn.so.sr.st.su.sv.sx.sy.sz.tc.td.tf.tg.th.tj.tk.tl.tm.tn.to.tr.tt.tv.tw.tz.ua.ug.uk.us.uy.uz.va.vc.ve.vg.vi.vn.vu.wf.ws.ye.yt.za.zm.zw".split(".");
var TLD_url = "https://gitcdn.xyz/repo/umpirsky/tld-list/master/data/en/tld.json";
var TLD_cache = "./tld.json";
var TLD_refresh_msec = 15 * 24 * 60 * 60 * 1000;
var TLD_last_msec;
var TLD_default_filter=function(dom){return dom.substr(0,3)!="xn-"};
function getTLDs(cb,filter_func){
if (typeof cb!=='function') return TLDs;
var
read,fetch,
CB_WRAP=function(tlds){
return cb(
filter_func===false
? cb(tlds)
: tlds.filter(
typeof filter_func==='function'
? filter_func
: TLD_default_filter)
);
},
check_mtime = function(mtime) {
if (Date.now()-mtime > TLD_refresh_msec) {
return fetch();
}
if (TLDs) return CB_WRAP (TLDs);
return read();
};
fetch = function(){
getJSON(TLD_url,function(err,data){
if (err) {
console.log("exception in getTLDs.fetch:",err.message||err);
return read(true);
} else {
TLDs=Object.keys(data);
fs.writeFile(TLD_cache,JSON.stringify(TLDs),function(err){
if (err) {
// ignore save error, we have the data
CB_WRAP(TLDs);
} else {
// get mmtime for the file we just made
fs.stat(TLD_cache,function(err,stats){
if (!err && stats) {
TLD_last_msec = stats.mtimeMs;
}
CB_WRAP(TLDs);
});
}
});
}
});
};
read=function(bypassFetch) {
fs.readFile(TLD_cache,'utf-8',function(err,json){
try {
if (err) {
if (bypassFetch) {
// after a http errror, we fallback to hardcoded basic list of tlds
// if the disk file is not readable
console.log("exception in getTLDs.read.bypassFetch:",err.messsage||err);
throw err;
}
// if the disk read failed, get the data from the CDN server instead
return fetch();
}
TLDs=JSON.parse(json);
if (bypassFetch) {
// we need to update stats here as fetch called us directly
// instead of being called by check_mtime
return fs.stat(TLD_cache,function(err,stats){
if (err) return fetch();
TLD_last_msec =stats.mtimeMs;
return CB_WRAP(TLDs);
});
}
} catch (e){
// after JSON error, if we aren't in an http fail situation, refetch from cdn server
if (!bypassFetch) {
return fetch();
}
// after a http,disk,or json parse error, we fallback to hardcoded basic list of tlds
console.log("exception in getTLDs.read:",err.messsage||err);
TLDs=TLDs_fallback;
}
return CB_WRAP(TLDs);
});
};
if (TLD_last_msec) {
return check_mtime(TLD_last_msec);
} else {
fs.stat(TLD_cache,function(err,stats){
if (err) return fetch();
TLD_last_msec =stats.mtimeMs;
return check_mtime(TLD_last_msec);
});
}
}
getTLDs(console.log.bind(console));
Hi all I am trying to display the words "hello JSON" on my web page but it's displaying the full string { "msg": "Hello JSON." }. I know why it is doing that, but how can I get it to display just hello JSON without just plopping it into an html script and using just that?
This is my code:
var http = require('http');
var domain = require('domain');
var root = require('./message'); // do I have to replace root w/ message
var image = require('./image'); // for better readability?
function replyError(res) {
try {
res.writeHead(500);
res.end('Server error.');
} catch (err) {
console.error('Error sending response with code 500.');
}
};
function replyNotFound(res) {
res.writeHead(404);
res.end('not found');
}
function handleRequest(req, res) {
console.log('Handling request for ' + req.url);
if (req.url === '/') {
root.handle(req, res);
}
if (req.url === '/image.png'){
image.handle(req, res);
} else {
replyNotFound(res);
}
}
var server = http.createServer();
server.on('request', function(req, res) {
var d = domain.create();
d.on('error', function(err) {
console.error(req.url, err.message);
replyError(res);
});
d.run(function() { handleRequest(req, res)});
});
server.listen(5000);
then message.js or root (in min var root = require(/message)
var http = require('http');
var body;
exports.handle = function(req, res) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Content-Length': body.length
});
res.end(body);
};
exports.init = function(cb) {
require('fs').readFile('app.html', function(err, data) {
if (err) throw err;
body = data;
cb();
});
}
app.html
<html>
<header><title>This is title</title></header>
<body>
Hello world
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
Make a request
</span>
<script type="text/javascript">
(function() {
var httpRequest;
document.getElementById("ajaxButton").onclick = function() { makeRequest('message.js'); };
function makeRequest(url) {
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
try {
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
if (!httpRequest) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
httpRequest.onreadystatechange = alertContents;
httpRequest.open('GET', url);
httpRequest.send(msg);
}
function alertContents() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
})();
</script>
</body>
</html>
Try changing body = data; to body = data.msg; in you 'message.js' file. This will lookup the value for the key, msg, in the JSON object and assign the result to the body variable.
edit
In your alertContents function, replace:
alert(httpRequest.responseText);
with:
var resData = JSON.parse(httpRequest.ResponseText);
alert(resData.msg);
What this does is parse the JSON response into a Javascript object and then looks up the msg attribute and uses that for the alert.
edit 2
Any time that you need to parse JSON, use JSON.parse(jsonString). It will return a native JavaScript object that you can store in a variable and look up attributes on, as in the example above.
I cannot see where you're receiving the msg from but at the place where you receive the JSON string just do this.
var jsonResult = receivedData();
if (jsonResult && jsonResult.msg)
{
alert(jsonResult.msg);}
}