Node.js extract value from callback function - javascript

I have a server file with a switch using the URL to display appropriate content. One of the cases is /users which should display a JSON string of a certain table. This is returned from a mysql file.
server.js
var http = require('http')
var url = require('url')
var port = 8080
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname
console.log('Request for ' + pathname + ' received.')
response.writeHead(200, {'Content-Type': 'text/html'})
response.write(run(pathname))
response.end()
}
function run(pathname) {
switch(pathname) {
case '/':
response = 'Welcome to my little test'
break
case '/time':
response = 'The time is ' + new Date().toLocaleTimeString()
break
case '/users':
var response
require('./mysql').getUsers(function(users) {
console.log(users)
response = users
})
return response
break
default:
response = 'Unable to locate the requested page'
}
return response
}
http.createServer(onRequest).listen(port)
console.log('Server started on port ' + port + '.')
mysql.js
var mysql = require('mysql')
var connection = mysql.createConnection({
user: "root",
password: "password",
database: "main"
})
exports.getUsers = function(callback) {
connection.query('SELECT * FROM users;', function (error, rows, fields) {
callback(JSON.stringify(rows));
});
};
The console.log(users) in server.js displays the JSON string fine, but I cannot figure out how to get the value out of the callback and into the response variable.
Any ideas will be greatly appreciated.

The way you could extract the value out of the callback is to assign that value to a variable out of the callback's scope, but I don't recommend you to do that since you would end up with lots of global variables, besides you don't know when the variable will be assigned. Try this and see what happens so you get some insight with how callbacks and node.js works:
function run(pathname) {
switch(pathname) {
case '/':
response = 'Welcome to my little test'
break
case '/time':
response = 'The time is ' + new Date().toLocaleTimeString()
break
case '/users':
var response
var out_of_callback_users
require('./mysql').getUsers(function(users) {
out_of_callback_users = users
console.log("In the callback")
console.log(users)
response = users
})
console.log("After require");
console.log(out_of_callback_users) //Users have not been assigned yet
setTimeout(function(){
console.log("In the timeout")
console.log(out_of_callback_users)
},5000) //After 5 secs the query has been completed and users have been assigned.
return response
break
default:
response = 'Unable to locate the requested page'
}
return response
}
The way I would go is something like this:
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname
console.log('Request for ' + pathname + ' received.')
response.writeHead(200, {'Content-Type': 'text/html'})
run(pathname, function(response){
response.write(response)
response.end()
})
}
function run(pathname,cb) {
switch(pathname) {
case '/':
cb('Welcome to my little test');
break;
case '/time':
cb('The time is ' + new Date().toLocaleTimeString());
break;
case '/users':
require('./mysql').getUsers(function(users) {
console.log(users);
cb(users);
})
break;
default:
cb('Unable to locate the requested page');
}
return;
}
http.createServer(onRequest).listen(port)
console.log('Server started on port ' + port + '.')

you can't do it like this. the problem is easy. let's talk about it:
function getUsers is an asynchronous. so the code follow runs like this:
case '/users':
var response
require('./mysql').getUsers(function(users) {
console.log(users)
response = users
})
return response
break
first, run require('./mysql').getUser() , then it will do return response directly, then break . when the getUser function is finished, it will run
function(users) {
console.log(users)
response = users
})
so, a rule you need to follow: once you use asynchronous, the other function have to be asynchronous.
i wonder you can modify like follow:
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname
console.log('Request for ' + pathname + ' received.')
response.writeHead(200, {'Content-Type': 'text/html'})
run(pathname, function(res){ response.write(res)}) //changed
response.end()
}
function run(pathname, callback) {
switch(pathname) {
case '/':
callback('Welcome to my little test')
break
case '/time':
callback('The time is ' + new Date().toLocaleTimeString())
break
case '/users':
var response
require('./mysql').getUsers(function(users) {
console.log(users)
callback(users) # changed
})
break
default:
callback('Unable to locate the requested page')
}
}
http.createServer(onRequest).listen(port)
console.log('Server started on port ' + port + '.')

You do not need to serialize the mysql returned rows to use it. Either you can process it within getUsers, or return it back to the controller. If you return it, change code to:
exports.getUsers = function(callback) {
connection.query('SELECT * FROM users;', function (error, rows, fields) {
callback(rows);
});
};
Now within the server.js file, you can process the returned rows, like:
case '/users':
var response = ''
require('./mysql').getUsers(function(users) {
for (var i in users) {
var user = users[i];
var userId = user.id;
var userName = user.user_name;
response += "User - ID: "+userId+" Name: "+userName+"\n";
}
})
return response;
break;
You can process

Related

Sent a POST request with JS, and handling the data with Node

I'm adding a contact me section to a website. I want to be able to send the data from the forms with JS, and then receive and do something with the data with Node. I understand that there are frameworks and libraries that can handle this stuff, but I would like to build it from scratch so that I have a better understanding of what is happening.
I currently have a section of JS (see below) that is taking the form data, and sending it as a POST request to the node script, but I can't seem to wrap my head around what is happening with node, or how to receive the data with the node script. Any help in pointing me in the right direction is greatly appreciated.
const name = $(".name");
const email = $(".email");
const message = $(".message");
const submitButton = $(".submitButton");
const nameRegex = /([a-zA-Z\s-])/g;
const emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/g;
const messageRegex = /([a-zA-Z\s.,?!$%&])/gm;
const url = "../node/contactMeSubmissionHandler.js"
submitButton.click(function(){
let nameContents = name.val().match(nameRegex).join("");
let emailContents = email.val().match(emailRegex).join("");
let messageContents = message.val().match(messageRegex).join("");
// if (emailRegex.test(emailContents) == true) {
// let emailValid = emailContents;
// } else {
// console.log("Email is invalid");
// };
const data = {
email: emailContents,
name: nameContents,
message: messageContents
}
$.post(url, data, function(data, status){
console.log(`${data} and status is ${status}`);
})
})
I like to write from scratch too. Here is working code which is called from a command line to get a token.
// clientEx.js
var http = require('http');
var fs = require('fs');
const _SERVER = "dcsmail.net"; /* dcsmail.net */
// Callback function is used to deal with response
//
var callback = function (response)
{
// update stream with data
var body = '';
response.on('data', function(data) {
body += data;
});
response.on ('end', function()
{
// Data received completely.
fs.writeFileSync ("temp.lst", body, 'utf8');
// console.log ("clientEx.js received: " + body);
});
}
if ((process.argv[2] == null) || (process.argv[3] == null) || (process.argv[4] == null) || (process.argv[5] == null))
{
console.log ("clientEx.js usage:<user email> <user password> <destination> <GUID>");
}
else
{
var Ef_email = encodeURI (process.argv[2]);
var Ef_pass = encodeURI (process.argv[3]);
var Ef_dest = encodeURI (process.argv[4]);
var Ef_guid = encodeURI (process.argv[5]);
var post_data = ("f_email=" + Ef_email +
"\&" + "f_pass=" + Ef_pass +
"\&" + "f_dest=" + Ef_dest +
"\&" + "f_guid=" + Ef_guid);
// Options to be used by request
var options = {
host: _SERVER,
port: '80',
path: '/DCSM/tokenP10.php',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength (post_data)
}
};
// console.log ("clientEx.js using " + _SERVER + ":" + options.port + options.path);
// request the token from the host
try
{
var req = http.request (options, callback);
req.write (post_data);
req.end();
}
catch (error)
{
fs.writeFileSync ("temp.lst", "Host access failed\n", 'utf8');
}
}
You should be able to adapt that to your needs.
Use this code to create a server and check the log in console for different request attributes.
const http = require('http');
http
.createServer((request, response) => {
console.log(request);
response.end();
})
.listen(3000);
Make GET and POST request to http://localhost:3000/ and look for method, headers etc.
See more here and here.

Jasmine + Async functions

Here is my code:
'use strict';
var unitID = 0;
var getById = function(generalOptions, specificOptions) {
describe('API tests for: ' + specificOptions.name, function() {
var url = generalOptions.baseUrl + specificOptions.route;
// GET all items
it('= = = GET ALL test for ' + specificOptions.name + ' return status
code 200', function(done) {
generalOptions.request.get({
url: url
}, function(error, response, body) {
expect(response.statusCode).toBe(200);
expect(JSON.parse(body)).not.toBeFalsy();
if (specificOptions.route == '/devices/') {
var bodyJS = JSON.parse(body);
unitID = bodyJS.devices[0].id;
} else {
unitID = '';
}
console.log('Result 1 - ' + unitID);
done();
});
});
//GET by ID
it('= = = GET by ID test for ' + specificOptions.name + ' return status code 200', function(done) {
console.log('Result 2 - ' + unitID);
generalOptions.request.get({
url: url + unitID
}, function(error, response, body) {
expect(response.statusCode).toBe(200);
expect(JSON.parse(body)).not.toBeFalsy();
done();
});
});
})
};
module.exports = getById;
I need to wait, while unitID will be updated with first GET request and then use in in the next request.
The problem is, that it works asynchronously and unitID in the second request stay 0.
Can show how to implement solution with async/await or Promises?
Thanks!
For debugging reason I do console.log. For now it print:
Result 2 - 0
Result 1 - 59dffdgfdgfg45545g
You should not write test in such fashion where output of one test goes into other.Each "it" should be independent.
Instead you should make call twice(nested call) to achieve the value of unitID or ideally you should mock the service to return the data that is expected by the "it".

Node.js http.request Results Back as Variable

All,
I am trying to figure out how to pass the results from an https.request in node.js code out to a variable. I have an https.request setup that correctly passes the correct information to a SOAP API and gets the correct response back. My ultimate goal is to get the output from the https.request into a variable that I can call using Express.
Here is are my code chunks.
HTML:
<div class="row">
<div class="col-md-12" class="pull-left">
<p> TEST </p>
<p>{{soapreply}}</p>
</div>
JS:
app.post('/cucmmapper/submit', function (req, res) {
// FORM - DATA COLLECTION
var cucmpub = req.body.cucmpub;
var cucmversion = req.body.cucmversion;
var username = req.body.username;
var password = req.body.password;
var authentication = username + ":" + password;
var soapreplyx = '';
// SOAP - BUILD CALL
var https = require("https");
var headers = {
'SoapAction': 'CUCM:DB ver=' + cucmversion + ' listCss',
'Authorization': 'Basic ' + new Buffer(authentication).toString('base64'),
'Content-Type': 'text/xml; charset=utf-8'
};
// SOAP - AXL CALL
var soapBody = new Buffer('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/11.5">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<ns:listCss sequence="?">' +
'<searchCriteria>' +
'<name>%</name>' +
'</searchCriteria>' +
'<returnedTags uuid="?">' +
'<name>?</name>' +
'<description>?</description>' +
'<clause>?</clause>' +
'</returnedTags>' +
'</ns:listCss>' +
'</soapenv:Body>' +
'</soapenv:Envelope>');
// SOAP - OPTIONS
var options = {
host: cucmpub, // IP ADDRESS OF CUCM PUBLISHER
port: 8443, // DEFAULT CISCO SSL PORT
path: '/axl/', // AXL URL
method: 'POST', // AXL REQUIREMENT OF POST
headers: headers, // HEADER VAR
rejectUnauthorized: false // REQUIRED TO ACCEPT SELF-SIGNED CERTS
};
// SOAP - Doesn't seem to need this line, but it might be useful anyway for pooling?
options.agent = new https.Agent(options);
// SOAP - OPEN SESSION
var req = https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (d) {
soapreplyx = d;
console.log("Got Data: " + d);
});
});
// SOAP - SEND AXL CALL
req.write(soapBody);
res.render('cucmmapper-results.html'), {
'title': 'CUCM 2.1',
'soapreply': soapreplyx
};
req.end();
req.on('error', function (e) {
console.error(e);
});
});
}
The line "console.log("Got Data: " + d)" is getting the correct expected reply from the API, however, I can't figure out how to get that data into my variable "soapreplyx" which changes in Express to "soapreply".
Much appreciated for any help you might have!
You're not waiting for your request to respond before you call res.render(), so the value of soapreplyx is always '', its initial value. To correct this, add an 'end' event listener on the response object passed to your https.request() callback.
You're not appending the chunks of the response to your soapreplyx variable, you're reassigning its value with each successive chunk.
let soapRequest = https.request(options, soapResponse => {
soapResponse.on('data', chunk => {
soapreplyx += chunk
})
soapResponse.on('end', () => {
return res.render('cucmmapper-results.html', {
title: 'CUCM 2.1',
soapreply: soapreplyx
})
})
})
soapRequest.write(soapBody)
soapRequest.end()

Two if statements in node js

In the following code, I want to read the parameters from the link and display them. I used two separate if statements to check each parameter. The problem is it only reads the first if statement and discards the second one (does not check the second parameter):
var http = require('http');
var url = require('url');
var server = http.createServer(function (request, response) {
if (request.method == 'GET') {
var queryData = url.parse(request.url, true).query;
response.writeHead(200, {"Content-Type": "text/plain"});
// if parameter is provided
if (queryData.name) {
response.end('Hello ' + queryData.name + '\n');
} else {
response.end("Hello World\n");
}
if (queryData.age) {
response.end('Your age is ' + queryData.age + '\n');
} else {
response.end("No age provided\n");
}
}
});
// Listen on port 3000, IP defaults to 127.0.0.1 (localhost)
server.listen(8081);
The link is:
http://127.0.0.1:8081/start?name=john&age=25
Do not use response.end. Instead use response.write and add an response.end() after both if-statements.
See docs: https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback
Try response.write in the first if block.

Node.js Callback Issues

Been working on a Node.js restful web service that is hosted on OpenShift. Currently I have had success with simple method calls and such, but can not seem to get the http response to work through an asynchronous callback.
Here is what I currently have:
var http = require("http");
var url = require("url"); // used to get the requested method name as well as parameters
var util = require("util");
// global variables
// router function
function route(pathname, query, callbackFunc) {
//return executeMethod(pathname, query);
var returnValue;
switch (pathname.toUpperCase()) {
case "/ADD":
returnValue = add(query['num1'], query['num2']);
//util.process.nextTick(function() {
//callbackFunc(null, returnValue);
//});
break;
case "/ADDASYNC":
//addAsync(query['num1'], query['num2'], callback);
break;
default:
returnValue = "method not found";
break;
}
//return returnValue;
//return "Route for request " + pathname + " complete, query: " + query;
}
// actual web method execution
function add(num1, num2){
//return "add method called with values: " + num1 + " " + num2;
return parseFloat(num1) + parseFloat(num2);
}
function addAsync(num1, num2, callback){
//util.process.nextTick(function(){
// var value = parseFloat(num1) + parseFloat(num2);
// util.process.nextTick(function(){
// callback(value);
// });
//});
}
// main request handler for server
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
var query = url.parse(request.url, true).query;
console.log("Request for " + pathname + " Recieved");
response.setTimeout(500);
var myCallback = function(err, data){
if(err){
response.writeHead(200, {"Content-Type": "text/plain"});
response.write('an error occured with requested method');
response.end();
}else{
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(data);
response.end();
}
}
//var finalValue = route(pathname, query);
//var finalValue = 0;
(function(){route(pathname, query, myCallback)})();
response.writeContinue();
//process.nextTick(myCallback(null, 'hello world'));
setTimeout(function(){
myCallback(null, "hello world");
}, 15);
//myCallback();
//response.writeHead(200, {"Content-Type": "text/plain"});
//response.write("Hello World. You requested: " + pathname + " with type " + pathname.type + ", value: " + finalValue);
//response.end();
}
// create the server and signal console of start
http.createServer(onRequest).listen(8080, process.env.OPENSHIFT_INTERNAL_IP);
// for debug
//http.createServer(onRequest).listen(process.env.PORT, process.env.IP);
console.log("Server has started. Listening to port: " + 8080 + " ip address: " + process.env.OPENSHIFT_INTERNAL_IP);
If I call the myCallback method directly inside the onRequest method, then I get a response back without any issues; however, calling the myCallback function inside the onRequest or route methods using process.nextTick or setTimeout does not seem to be working. I am working on this project using the Cloud9 IDE with direct git push to OpenShift so I am having some difficulties with my debug but have tried quite a few different approaches with no success, including setting the request.setTimeout function to provide some time for the timer/process event to fire. My current OpenShift app is running Node.js 0.6. Is there anything Obvious that could be causing issues that I might be missing?
I got your setTimeout to work by doing this:
comment out "response.setTimeout(500);" on line 54. It's invalid.
comment out "(function(){route(pathname, query, myCallback)})();" on line 71. Also invalid.
change timeout time to 5000 on line 76 (5000ms = 5 seconds)
For nextTick to work:
everywhere only do "process.nextTick" not "util.process.nextTick".
change line 16 to: "returnValue = add(query['num1'], query['num2']).toString();" (have to cast it as a string!)
uncomment 17, 18, 19 to see this will now work
comment out line 54, you don't need this
change line 70 to "route(pathname, query, myCallback);"
You should see what you did wrong now.

Categories