totally new to Node.js and this callback thing is driving me nuts.
I am writing a Skill for an Amazon Echo. As part of this I am trying to send an SMS using BulkSMS.com via a HTTP Request. The http.request has a callback which parses the response. (To take the BulkSMS API out of the equation in the example below I am just trying to get it working using a http request to Random.org (i.e. www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new)
However I am getting an error saying that the callback is not defined.
{
"errorMessage": "callback is not defined",
"errorType": "ReferenceError",
"stackTrace": [
"Emergency.eventHandlers.onLaunch (/var/task/index.js:54:11)",
"AlexaSkill.requestHandlers.LaunchRequest (/var/task/AlexaSkill.js:16:37)",
"AlexaSkill.execute (/var/task/AlexaSkill.js:97:24)",
"exports.handler (/var/task/index.js:100:15)"
]
}
I am sure I am doing something totally stupid and for that I apologize, but I just can't see it.
Here is the onLaunch event handler in my index.js. Any help greatly appreciated.
Emergency.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
console.log("Emergency onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
console.log("Attempting to send SMS");
callback = function(response) {
var str = '';
console.log("In callback");
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
console.log("Getting Data");
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
console.log("End of response");
console.log(str);
});
}
http.request(options, callback).end();
console.log("Finished sending SMS");
var speechOutput = "<speak>SMS sent</speak>";
response.tell(speechOutput);
};
Full index.js is below.
'use strict';
/**
* App ID for the skill
*/
var APP_ID = "amzn1.ask.skill.eb8cf94a-848f-45ae-9792-xxxxxxxxxx";
/**
* The AlexaSkill prototype and helper functions
*/
var AlexaSkill = require('AlexaSkill');
var http = require('http');
var request = require("request");
//var Alexa = require('alexa-sdk');
var SKILL_NAME = 'Emergency';
var Emergency = function () {
AlexaSkill.call(this, APP_ID);
console.log("APP_ID set");
};
var alexaResponse = "";
//The url we want is: 'www.random.org/integers/? num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
// Extend AlexaSkill
Emergency.prototype = Object.create(AlexaSkill.prototype);
Emergency.prototype.constructor = Emergency;
Emergency.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
console.log("Emergency onSessionStarted requestId: " + sessionStartedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any initialization logic goes here
};
Emergency.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
console.log("Emergency onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
console.log("Attempting to send SMS");
callback = function(response) {
var str = '';
console.log("In callback");
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
console.log("Getting Data");
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
console.log("End of response");
console.log(str);
});
}
http.request(options, callback).end();
console.log("Finished sending SMS");
var speechOutput = "<speak>SMS sent</speak>";
response.tell(speechOutput);
};
Emergency.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
console.log("Emergency onSessionEnded requestId: " + sessionEndedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any cleanup logic goes here
};
Emergency.prototype.intentHandlers = {
// register custom intent handlers
EmergencyIntent: function (intent, session, response) {
// Get a random "never" phrase from the list
}
};
// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
// Create an instance of the Emergency skill.
var emergency = new Emergency();
emergency.execute(event, context);
};
However I am getting an error saying that the callback is not defined.
That's because of 'use strict'; mode.
First, strict mode makes it impossible to accidentally create global variables. In normal JavaScript mistyping a variable in an assignment creates a new property on the global object and continues to "work" (although future failure is possible: likely, in modern JavaScript). Assignments which would accidentally create global variables instead throw in strict mode:
More on this in MDN
accidentalGlobalVariable = 'hello world';
console.log(accidentalGlobalVariable); // logs hello world
In strict mode,
'use strict';
accidentalGlobalVariable = 'hello world';
console.log(accidentalGlobalVariable); // errors out
Error
accidentalGlobalVariable = 'hello world';
^
ReferenceError: accidentalGlobalVariable is not defined
In your code sample, If I run without strict mode it works,
var http = require('http');
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function (response) {
console.log('response statusCode', response.statusCode);
};
http.request(options, callback).end();
but if I run with strict mode,
'use strict';
var http = require('http');
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function (response) {
console.log('response statusCode', response.statusCode);
};
http.request(options, callback).end();
I too get error because strict mode errors out callback
callback = function (response) {
^
ReferenceError: callback is not defined
you can remedy it by
Using var or let in assignment.
var callback = function (response) {
console.log('response statusCode', response.statusCode);
};
Eliminating the need of callback all together and use an anonymous function instead.
http.request(options, function (response) {
console.log('response statusCode', response.statusCode);
}).end();
Related
I am new to using async/await and having a couple issues.
I have the code below, which seems to not wait until the previous function is finished?
var url = require('url');
var path = require('path');
var https = require('https');
var request = require('request');
var url1 =
var url2 =
var url3 =
module.exports = async function (context, req) {
var call = await callUrl(context, url1);
context.log(call);
var call2 = await callUrl(context, url2);
context.log(call2);
var call3 = await callUrl(context, url3);
context.log(call3);
};
function callUrl (context, web) {
var requestUrl = url.parse(web);
const requestOptions = {
hostname: requestUrl.hostname,
path: requestUrl.path,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
var request = https.request(requestOptions, function(res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
var jsonData = JSON.parse(data);
return jsonData;
});
}).on('error', function(error) {
context.log("request error:", error);
return context.done();
});
request.end();
}
I am trying to get call to happen, then when it is finished call2, then when that is finished call3.
Can someone pinpoint why this does not occur? Currently, it hits all 3 pretty much asap, and each context.log is undefined presumably because the endpoints don't return anything. Each url is another azure function app API I have created.
There is nothing I am requiring to return from each call to use, I simply want them to finish before moving on the the next function.
Your callUrl method, which you call with await, needs to be either async itself or return a Promise. Why? because the work it does is itself asynchronous.
Here's your function adapted to use a Promise and return its actual value via the resolve() callback.
function callUrl (context, web) {
var requestUrl = url.parse(web);
const requestOptions = {
hostname: requestUrl.hostname,
path: requestUrl.path,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
return new Promise(function (resolve,reject) {
var request = https.request( requestOptions, function( res ) {
var data = "";
res.on( 'data', function( chunk ) {
data += chunk;
} );
res.on( 'end', function() {
var jsonData = JSON.parse( data );
resolve( jsonData );
} );
} )
.on( 'error', function( error ) {
reject(error);
} );
request.end();
});
}
Notice that you use a POST operation with no body. That's a little unconventional.
I'm fumbling my way through node.js with massive help from people on here and I'm struggling getting the body of a GET request into a variable.
Here's the code so far:
var speechOutput;
var myCallback = function(data) {
console.log('got data: '+data);
speechOutput = data;
};
var usingItNow = function(callback) {
var http = require('http');
var url = 'http://services.groupkt.com/country/get/iso2code/IN';
var req = http.get(url, (res) => {
var body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
var result = JSON.parse(body);
callback(result);
});
}).on('error', function(e){
console.log("Got an error: ", e);
});
};
usingItNow(myCallback);
I'm using examples from other posts to try and get the body of the GET request into the speechOutput variable but it is coming out as undefined.
Ultimately I want the RestResponse.result.name in speechOutput, but I thought I would take this one step at a time. Can anyone offer any pointers?
Further to this, I have tried the following, which still came back undefined - maybe there is a bigger issue with the code? It doesn't even seem to be getting to the parse.
res.on("end", () => {
// var result = JSON.parse(body);
callback('result');
});
putting the line callback('result'); before the line var req = http.get(url, (res) => { returns 'result' but anything else is either undefined or causes an error.
Quoting Roy T. Fielding:
Server semantics for GET, however, are restricted such that a body,
if any, has no semantic meaning to the request. The requirements
on parsing are separate from the requirements on method semantics.
Don't use get request to send body parameters. Use post requests. If you want to send data within a get request, add them to the query string.
Read this for more info about bodies in get requests:
HTTP GET with request body
Update:
Try to log errors in the response, add this before you set up the listeners:
var body = "";
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}
res.on("data", (chunk) => {
body += chunk;
});
I'm trying to execute the following code inside AWS Lambda which only makes a POST http request to an ElasticSearch.
The problem I'm facing is that it seems the nodejs request has a read timeout and the response is almost always cut and an error is thrown. I've checked that the problem is not related with AWS Lambda timeout which is set to 10 seconds and the code throws an error in less than a second.
As you can see, I've tried to put a timeout to 5secs but I think that's a connection timeout and not a read timeout.
What am I doing wrong?
var http = require('http');
exports.handler = (event, context, callback) => {
var options = {
hostname: '172.31.40.10',
port: 9200,
path: '/articles/es/_search?_source=reference',
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (body) {
var parsed = JSON.parse(body);
var b = [];
for (var i = 0; i < parsed.hits.hits.length; i++) {
b.push(parsed.hits.hits[i]._source.reference);
}
var response = {
statusCode: '200',
body: JSON.stringify(b),
headers: {
'Content-Type': 'application/json',
}
};
callback(null, response);
});
});
req.on('error', function(e) {
callback(new Error('fallo'));
});
req.setTimeout(5000, function() {req.abort;})
req.on('socket', function (socket) {
socket.setTimeout(5000);
socket.on('timeout', function() {
req.abort();
});
});
req.write(MY_QUERY_HERE);
req.end();
};
I think you should let the stream of incoming data finish before performing any data manipulation.
Example :
var http = require('http');
//var _ = require('underscore');
function MyPostRequest(callback) {
var options = {
hostname:'172.31.40.10',
port:9200,
path:'/articles/es/_search?_source=reference',
method:'POST',
headers:{'Content-Type':'application/json'}
};
http.request(options, function(res) {
var tmpstore = ''; //temp. data storage
//:Store the continuous incoming data stream
res.on('data', function(d){tmpstore += d;});
//:Data reception is done, use it now...
res.on('end', function() {
var parsed = JSON.parse(tmpstore); var b = [];
for (var i = 0; i < parsed.hits.hits.length; i++) {
b.push(parsed.hits.hits[i]._source.reference);
}
/* //I suggest using underscore module :
_.each(parsed.hits.hits,function(element, index, list){
b.push(element._source.reference);
});
*/
var response = {
statusCode:'200',
body:JSON.stringify(b),
headers:{'Content-Type':'application/json'}
};
callback(null, response);
});
//:Response contained an error
res.on('error', function(e){/*error handling*/callback(e,null);});
});
}
I have a RESTish api on a node js server. When it receives a GET request, it is to call a function that will then be calling another server get function. The code looks like this:
var server = http.createServer(function(request, response) {
console.log("YEAHHH! ", request.method);
var string='';
// Inside a request handler method
if (request.method == "OPTIONS") {
console.log("options");
// Add headers to response and send
//response.writeHead(statusCode, responseHeaders);
response.writeHead(success,responseHeaders);
response.end();
}
if(request.method == "GET") {
string = soso();
}
console.log("*******", string);
response.writeHead(success,responseHeaders);
response.end(string);
});
soso() is the call to the other server. The issue is I want to send the response of the soso() function before its finished so all I'm getting is an empty string.
How do I get around this?
I'm sure this is a duplicate but can't quite find what I'm looking for. So, any help is appreciated.
EDIT
Code for the soso function:
var soso = function () {
console.log("this is being called");
var options = {...}
var req = https.get( options, function(res) {
var str = '';
res.on('data', function ( chunk ) {
str += chunk;
})
res.on('end', function () {
console.log ( "str is: ", str );
string = str;
})
req.end();
console.log(res.statusCode);
console.log(responseHeaders);
});
}
You can try something like this , I am not sure. I have not tested this. Pass the response object in soso function, then use response.end(string); after get request gets end.
var server = http.createServer(function(request, response) {
console.log("YEAHHH! ", request.method);
var string='';
// Inside a request handler method
if (request.method == "OPTIONS") {
console.log("options");
// Add headers to response and send
//response.writeHead(statusCode, responseHeaders);
response.writeHead(success,responseHeaders);
response.end();
}
if(request.method == "GET") {
string = soso(response); //send response object as argument
} else {
console.log("*******", string);
response.writeHead(success,responseHeaders);
response.end(string);
}
});
soso function
var soso = fuction(response){ // we pass the response object in soso function
console.log("this is being called");
var options = {...}
var req = https.get( options, function(res) {
var str = '';
res.on('data', function ( chunk ) {
str += chunk;
})
res.on('end', function () {
console.log ( "str is: ", str );
string = str;
})
req.end();
response.writeHead(success,responseHeaders);
response.end(string);
console.log(res.statusCode);
console.log(responseHeaders);
});
}
While trying to debug the following get request, I notice that it returns undefined and then runs the code for the response.
configs is a json object with all the parameters defined. I am also, for some reason, getting a response form the php server saying that grant-type is invalid or can't be found, although when debugging it is passing the correct parameter from the configs file.
How can I correct my code?
var http = require("http");
var querystring = require("querystring");
var _ = require("underscore");
apiCaller = {};
apiCaller.token = null;
var server=http.createServer(function(req,res){});
server.listen(8080);
apiCaller._get = function (context, config, fn) {
// request to obtain our oauth token
var options = {
method: "GET",
hostname: config.host,
client_id: config.clientId,
client_secret: config.clientSecret,
grant_type: config.grant_type,
path: "/my/path/to/token",
headers : {
'Content-Type': "application/json",
'Accept': "application/json"
}
};
var callback = function(response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
var str = '';
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
// error response
response.on("error", function (error) {
if ( !context ) {
console.error("Something went wrong with the api response.");
return;
}
context.done(new Error("Something went wrong with the api response."));
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
apiCaller.token = JSON.parse(str).access_token;
// we want to stop the request if token is not correct
if ( !apiCaller.token || apiCaller.token === undefined || apiCaller.token === null ) {
if ( !context ) {
console.error("Something went wrong with the token. Wrong token! Token: %s", apiCaller.token);
return;
}
console.error("Token: %s", apiCaller.token);
context.done(new Error("Something went wrong with the token. Wrong token!"));
}
});
};
var request = http.request(options, callback);
request.on('error', function(e) {
console.log('problem with request:');
});
request.end();
};
It is an asynchronous function. Asynchronous functions (which are kind of the bread-and-butter of Node.js) typically return nothing. Instead, what you might think of as the return value is passed to the callback function. That's what's happening here.
As Trott says, It's asynchronous, it's possible that request.end() is executing before callback function has finished....