I have some really strange problem with nodejs and express.
One of my functions that handles request, have to get something from DB and send it as JSON to client.
So it goes like this:
Get request
Call DB
Process data and pack it to JSON
response.json(JSON)
Normally it will go all OK but if there is timeout between 2 and 3 because it is asynchronously it will automatically create response and there will be error "Can\'t set headers after they are sent." when I call 4
Does anyone else have this problem? Is there any normal way to handle it or I just have to check if response._header is allready set?
exports.appstimebygroup = function (req, res) {
var resp = {};
var clientId = Webapi.extractClientId(req);
AppTime.getByGroupId(clientId, req.body.groupId, function(error, appstime){
if (error) {
handleError(error);
resp.returnCode = 0;
resp.message = "Some error have happened, please contact support!";
res.setHeader("Content-Type", "application/json");
res.json(resp);
return;
}
resp.returnCode = 1;
resp.appstime = appstime;
if(res._header){
console.log("header allready set!");
return;
}
res.setHeader("Content-Type", "application/json");
res.json(resp);
});
};
And AppTime.getByGroupId has asynchronous call inside.
Ok, problem is multipart-form-data handling timeout.
When that happens it calls next(err).
form.on('error', function(err){
if (!options.defer) {
err.status = 400;
next(err);
}
done = true;
});
By default on error it will do res.send(400) and when normally gets to code that you wanted to be executed there is problem.
Related
I am trying to store the response of an http request made using nodejs by request module but the problem is I can't acsess it after the request is completed in more details we can say after the callback
How I can add it
Here is what I tried till now
Tried to use var instead of let
Tried passing it to a function so that i can use it later but no luck
Here is my code can anyone help actually new to nodejs that's why maybe a noob question
var request = require('request')
var response
function sort(body) {
for (var i = 0; i < body.length; i++) {
body[i] = body[i].replace("\r", "");
}
response = body
return response
}
request.get(
"https://api.proxyscrape.com/?request=getproxies&proxytype=http&timeout=10000&country=all&ssl=all&anonymity=all",
(err, res, body) => {
if (err) {
return console.log(err);
}
body = body.split("\n");
sort(body);
}
);
console.log(response)
In this I am fetching up the proxies from this api and trying to store them in a variable called as response
var request = require("request");
var response;
async function sort(body) {
await body.split("\n");
response = await body;
console.log(response); // this console log show you after function process is done.
return response;
}
request.get(
"https://api.proxyscrape.com/?request=getproxies&proxytype=http&timeout=10000&country=all&ssl=all&anonymity=all",
(err, res, body) => {
if (err) {
return console.log(err);
}
sort(body);
}
);
// console.log(response); //This console log runs before the function still on process, so that's why it gives you undefined.
Try this code it works fine I just tested.
put the console log inside the function so you can see the result.
The console.log that you put actually runs before you process the data so that's why you are getting "undefined".
Actually, you will get the data after the sort Function is done processing.
I am experimenting with the softlayer-client api wrapper in my Node Express application. My goal is to update the VPN password of a User_Customer by calling the updateVpnPassword method on a specific user.
I can construct a call to achieve a VPN password update using request, but I'm not sure it's the best way to achieve the desired result.
Can the softlayer-client module be used to make an similar call to this:
function updateVpnPassword(req, res, next) {
// Construct URL to update VPN
myURL = 'https://' + <userIDAdmin> + ':' + <apiKeyAdmin> + '#api.softlayer.com/rest/v3/SoftLayer_User_Customer/' + <softLayerID> + '/updateVpnPassword/' + <newPassword> + '.json';
request.get({url: myURL}, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});
next();
}
My initial attempts have been to try variations on this:
function updateVpnPassword(req, res, next) {
// Assuming var client = new SoftLayer();
client
.auth(<userIDAdmin>, <apiKeyAdmin>)
.path('User_Customer', <softLayerID>,'updateVpnPassword')
.parameters(<newPassword>)
.put(function(err,result){
console.log(result);
if (err) {
next(err); // Pass errors to Express.
}
else {
// update successful
}
});
next();
}
But the console log gives an error response like
{ message: { error: 'Internal Error', code: 'SoftLayer_Exception_Public' } }.
I expect a TRUE or FALSE response, to indicate the whether the update is successful.
A similar python client can be found here but I require an implementation in JS.
I'm not familiar with nodejs but I installed the package softlayer-node and run your second code and it worked.
I also created the following script and I got TRUE
var username = 'set me';
var apikey = 'set me';
var userId = 1111111;
var SoftLayer = require('softlayer-node');
var client = new SoftLayer();
client
.auth(username, apikey)
.path('User_Custome', userId, 'updateVpnPassword')
.parameters('P#ssword123')
.put()
.then(function(result) {
console.log(result);
}, function(error) {
console.log(error);
});
node command:
$ node updateVpnPassword.js
true
Did you tried by sending that request using curl or any other REST client like postman?
If you get the same error then I recommend you submit a ticket and provide information like the id of users you are trying to update the vpn password and the user with which you are sending the request.
In my MongoDB/Node backend I have a pretty standard function for adding a new language to the database via a POST request. It looks like this:
exports.add = async function(req, res) {
let request, doc;
request = new EndpointRequestController(req, res);
try {
doc = await request.save(Language, req.body);
} catch(err) {
return request.sendError('An error occurred while trying to find existing records.', err);
}
request.sendResponse(doc, res.status(201));
}
Currently, this is working - in that when the user passes a value in the body of the POST request, the new language is added to our languages collection.
What I'm trying to figure out is, why is this returning a "204" response, rather than the "201" status I'm explicitly sending here as part of the response? How can I edit this to send the "201" response?
You are sending response outside the await function... you need to put res.send inside await function
exports.add = async function(req, res) {
let request, doc;
request = new EndpointRequestController(req, res);
try {
doc = await request.save(Language, req.body);
res.status(201).send(doc)
} catch(err) {
return request.sendError('An error occurred while trying to find existing records.', err);
}
}
Overview
I'm developing an MVC application with NodeJS. When the application loads for the first time, the database object (using a pool) is created.
var pool = mysql.createPool({connectionLimit: 150, host: __host,
user: __user, password: __password,
database: __database})
module.exports = pool
When a request is received, a Controller object is created, which creates a Model to perform actions. The model gets a connection from the pool, performs the action, and releases the connection back to the pool.
//router snippet
router.get('/post_data', function(req, res){
router.setRequestAndResponse(req, res)
var post_data = new Post_Data()
post_data.processDataFromGet(router)
})
//controller code snippet
Post_Data_Controller.prototype.processDataFromGet = function(router){
var controller_obj = this
var data_array = {}
var req = router.req, res = router.res
//retrieving data from request and passing to the data_array
controller_obj.model.create(data_array, function(result){
var xml = xmlbuilder.create("response")
if (result.code == "error"){
xml.e("code", "error")
xml.e("message", result.error_message)
}else if (result.code == "success"){
xml.e("code", "success")
}
controller_obj.sendResponse(router.res, xml, "xml")
})
}
Post_Data_Controller.prototype.sendResponse = function(res, response, type){
if (type == "json"){
res.set({"Content-Type": "application/json", "Content-Length": JSON.stringify(response).length})
res.send(response)
}else{ /* Default type is XML */
res.set({"Content-Type": "application/xml", "Content-Length": response.end({pretty: true}).length})
res.send(response.end({pretty: true}))
}
}
//Model snippet
Post_Data.prototype.create = function(data_array, callback){
/* data validation */
var fail = false, error_data = {}
if (fail) {callback({code: "fail", cause: error_data}); return;}
//the next 2 lines do not throw an error when uncommented
//callback({code: "fail", cause: "how's it going"});
//return;
__db_pool.getConnection(function(err, db_conn){
// the next two lines throw an error for two or more requests coming in at the same time
callback({code: "fail", cause: "how's it going"});
return;
if (err) { callback({code: "error", error_message: err}); return;}
callback({code: "fail", cause: "how's it going"});
return;
db_conn.query("sql command", [data_array],
function(err, result){
if (err){ callback({code: "error", error_message: err}); return;}
if (result && result.length > 0){ //affiliate and listing exist
data_array.listing_id = result[0].listings_id
var data = [data_to_insert]
db_conn.query("sql command here", data,
function(err, result){
db_conn.release()
if (err){ callback({code: "error", error_message: err}); return;}
if (result && result.affectedRows > 0) {
callback({code: "success", data: {data_to_be_returned}})
}else {callback({code: "error", error_message:"Error inserting data"}); return}
})
}else{
callback({code: "fail", cause: "error to send back"})}
})
})
}
Problem
These requests are web service requests. If I send one GET request, no error happens; however, when I send two or more concurrent requests, I receive this error:
/project_path/node_modules/mysql/lib/protocol/Parser.js:82
throw err;
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:689:11)
at ServerResponse.res.set.res.header (/project_path/node_modules/express/lib/response.js:549:10)
I traced the culprit to the specific line in the Model code pasted above. It seems that for some reason, once the model obtains a connection from the pool for the second request, it somehow interferes with the first request. Both requests still insert the proper data into the database; however, the second and subsequent requests can't send a response without throwing an error anymore.
I have performed the requests with GET, POST, and PUT content-types; only GET throws the error. All the other content-types don't throw any error, even with over one thousand concurrent requests.
Here's the web service code for the GET requests; it's the same for the other content-types except for the content-type changes and the data being put in the body.
for(var i=0; i less than 5; i++){
sendAsGet()
i++
}
function sendAsGet(){
try{
var data = "?data_to_be_sent"
var uri =url.parse("http://localhost:4000/post_data")
var options = {hostname: uri.hostname, port: uri.port, method: "GET",
path: uri.path + data, agent: false}
request = (uri.protocol == "https")? https : http
var req = request.request(options, function(res){
var result = ""
console.log("STATUS: " + res.statusCode)
console.log("HEADERS: " + JSON.stringify(res.headers))
res.setEncoding("utf8")
res.setTimeout(50, null)
res.on("data", function(chunk){
result += chunk
})
res.on("end", function(){
console.log(result)
})
})
req.end()
}catch(err){
console.log(err.message)
}
}
I would like to know 2 things:
Why is getting the database connection causing this problem?
Why does it happen only on GET requests and not on POST and PUT?
Google and previous SO questions haven't been able to help so far.
Thanks.
The reason you are seeing the error is because you're placing request/response instances on the router itself. Don't do that. The router object is a "static" object, it's not a per-request thing. So currently this is what's happening (in order):
Request #1 comes in and sets req/res on router and starts the asynchronous model.create().
Meanwhile, request #2 comes in and overwrites req/res on router and starts its own asynchronous model.create().
Request #1's model.create() callback is called, sending the response to request #2's socket instead.
Request #2's model.create() callbacks is called, where it attempts to send a response to the same res that was just responded to just a moment ago. Trying to write headers to a response that has already been sent then results in the error you are seeing.
I'm using node js, express and postgresql as backend.
This is the approach I used to make a rest API:
exports.schema = function (inputs, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
query.exec(function (err, schemas) {
if(err){
var response = {
message: 'Something went wrong when trying to fetch schemas',
thrownErr: err
};
console.error(response);
res.send(500, response);
}
if(schemas.length === 0){
var message = 'No schemas was found';
console.error(message);
res.send(400, message);
return;
}
res.send(200, schemas);
});
};
It works but after a while postgres logs an error and it's no longer working:
sorry, too man clients already
Do I need a close each request somehow? Could not find any about this in the express docs. What can be wrong?
This error only occurs on production server. Not on developing machine.
Update
The app only brakes in one 'module'. The rest of the app works fine. So it's only some queries that gives the error.
Just keep one connection open for your whole app. The docs shows an example how to do this.
This code goes in your app.js...
var Knex = require('knex');
Knex.knex = Knex.initialize({
client: 'pg',
connection: {
// your connection config
}
});
And when you want to query in your controllers/middlewares...
var knex = require('knex').knex;
exports.schema = function (req, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
// more code...
};
If you place Knex.initialize inside an app.use or app.VERB, it gets called repeatedly for each request thus you'll end up connecting to PG multiple times.
For most cases, you don't need to do an open+query+close for every HTTP request.