I have an web app with using Twitter API. Main focus is to get the users that are not following back based on given twitter user name. It works fine until this point but when I get an error because of the fact that user name does not exist on Twitter, Server shuts down itself. Using Nodejs, expressjs.
The Error Message :
App is running on port : 3000
[ { code: 34, message: 'Sorry, that page does not exist.' } ]
_http_outgoing.js:494
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
at ServerResponse.header (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/express/lib/response.js:170:12)
at /home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/twitter-api.js:30:21
at Request._callback (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/twitter/lib/twitter.js:215:14)
at Request.self.callback (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/request/request.js:186:22)
at emitTwo (events.js:126:13)
at Request.emit (events.js:214:7)
at Request.<anonymous> (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/request/request.js:1163:10)
Problematic part of the code is below. The question is : How can I avoid this situation ? Or is it even possible ?
client.get('followers/ids', params, function(error, followers_results, response) {
if (error) {
res.send("error");
console.log(error);
}
let followers = followers_results.ids;
client.get('friends/ids', params, function(error, following_results, response ) {
if (error) {
res.send("error");
console.log(error);
}
let following = following_results.ids;
following.forEach(function(person){
if(followers.indexOf(person) === -1){
one_way_following.push(person);
}
});
// console.log(one_way_following);
one_way_following = one_way_following.slice(0, 100);
one_way_following_string = one_way_following.join();
// console.log("----------------------------------------------------");
// console.log(one_way_following_string);
// console.log("----------------------------------------------------");
client.get('users/lookup', {user_id: one_way_following_string}, function(error, user_results, response, next) {
if (error) {
res.send("error");
console.log(error);
}
user_results.forEach(function(user){
let userObject = {
name : user.name,
screen_name : user.screen_name,
avatar: user.profile_image_url
}
// console.log(user.name);
users.push(userObject);
})
res.render("results.ejs",{users:users});
// console.log(users);
});
});
});
This error is actually happening because, you can't call res.render("results.ejs",{users:users}); or res.send("error"); multiple times, because headers can't be changed once set.
Easiest solution would be to break out of the function once you've caught an error (if (error) { on line 2), or handle it appropriately (render something blank, or render your friendly error view).
For more information, about this, check out the answer to this question, Error: Can't set headers after they are sent to the client
Hope that helps :)
Related
I am trying to create a route so that when someone accesses it, it will download a file and send a response message to the user (should appear in the HTML). I am using the download function from express JS but it failed.
This is my code:
router.get('/api/zip', AuthMiddleware, (req, res) => {
//console.log("hello");
//return res.status(200).download(".\\views\\index.zip"); => download but doesn't send message to the user.
res.download(".\\views\\index.zip", "index.zip", (err) => {
if (err) {
res.status(500).send({
message: "Could not download the file. " + err,
});
} else {
try {
res.status(200).send("Download!");
} catch (error) {
res.status(500).send("Download fail 2");
}
}
});
});
After the user access http://localhost:4000/api/zip, it downloads the file and the server crashes:
[nodemon] starting `node index.js`
Listening on port 4000
_http_outgoing.js:561
throw new ERR_HTTP_HEADERS_SENT('set');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (internal/errors.js:322:7)
at ServerResponse.setHeader (_http_outgoing.js:561:11)
at ServerResponse.header (D:\MyProject\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (D:\MyProject\node_modules\express\lib\response.js:174:12)
at D:\MyProject\routes\index.js:34:21
at D:\MyProject\node_modules\express\lib\response.js:450:22
at SendStream.onend (D:\MyProject\node_modules\express\lib\response.js:1078:5)
at SendStream.emit (events.js:400:28)
at ReadStream.onend (D:\MyProject\node_modules\send\index.js:813:10)
at ReadStream.emit (events.js:412:35) {
code: 'ERR_HTTP_HEADERS_SENT'
I understand that I can't use download and then send, so how can I download and send message to the user on the HTML page?
You already know that you can't send 2 responses one after the other.
What can be done, is setting a custom header for your response, so something like this:
app.get('/download', (req, res) => {
res.append('message', JSON.stringify({ message : "hello"}) );
res.download("test.txt", "try.txt");
});
And then get the message from that, It feels really "hacky" though...
findByIdAndUpdate() successfully updates document, but returns error which i don't understand.
Here is schema:
const userSchema = mongoose.Schema({
phone: String,
password: String,
token: String
});
const User = mongoose.model('User', userSchema);
And here is function to update user in database
export const login = (req, res) => {
User.findOne({ phone: req.body.phone }, (err, result) => {
if (err) res.status(500).send(`User with ${req.body.phone} doesn't exist. \n Error: ${err}`);
if( result.password === req.body.password ){
// here Console.log(result) returns:
//{
// _id: 5aa28eb4f4a8de28c24e6990,
// phone: '+79781231233434',
// password: 'passsss',
// token: '1520613346284',
// __v: 0
//}
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send('Unable to create token. Error: ', err);
return res.status(200).send(result._id, result.token);
})
} else return res.status(500).send('Incorrect password');
})
}
Here is what i get in console when i do post request with data which should successfully pass this check and get token.
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:17:28
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:16:37
/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:423
throw err;
^
RangeError: Invalid status code: Unable to create token. Error:
at ServerResponse.writeHead (_http_server.js:190:11)
at ServerResponse._implicitHeader (_http_server.js:181:8)
at write_ (_http_outgoing.js:635:9)
at ServerResponse.end (_http_outgoing.js:754:5)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:221:10)
at ServerResponse.json (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:158:21)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/controllers/user.js:10:38
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/model.js:3930:16
at _init (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2000:14)
at completeOne (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:1995:5)
at cb (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2365:14)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2465:14
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:418:16
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at session.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:400:11)
at ClientSession.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb-core/lib/sessions.js:69:41)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:396:17)
at handleCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:128:55)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/collection.js:2302:12
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:405:9)
[nodemon] app crashed - waiting for file changes before starting...
I don't understand this error. Why it appears? If update done, why there is an error at all? And why i see warning about deprecations if i use res.status(status).send(body), as it suggests, everywhere? Maybe it's Babel makes problems to me? Anybody know what i need to check to figure out whats going on?
The body parameter can be a Buffer object, a String, an object, or an Array.
For it to work properly, use string interpolation:
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send(`Unable to create token. Error: ${err}`);
return res.status(200).send(`${result._id}, ${result.token}`);
})
Source: node-express error : express deprecated res.send(status): Use res.sendStatus(status) instead
so I'm posting this question after a hours of research .... That's the answer of the browser and I can't find any reason why...
So, after the login, I want to fetch the score of the user logged. The score is located in a subtable of the user table (Mongo ...). But the call for the server doesn't work. Here is my code :
login.component.ts
this.dbService.scoresDownload(this.username).subscribe(scoresDownload => {
console.log(scoresDownload);
});
db.service.ts
scoresDownload(username) {
this.createAuthenticationHeaders();
console.log(username)
return this.http.post(this.domain + 'training/scoresDownload', username, this.options).map(res => res.json());
}
training.js (route in Express)
router.post('/scoresDownload', (req, res) => {
// let wordScoresDb = [];
console.log(req)
User.find({}, (err, result) => {
if (err) {
res.json({ success: false, message: err }); // Return connection error
} else {
res.json({ success: true, message: result }); // Return as vailable username
}
})});
It works well if I use a get instead of a post .... And the most tricky part is that it works with all my other calls. So, I really don't know where I mess up ...
Cheers guys !
EDIT :
I forgot to mention the error sent by express
SyntaxError: Unexpected token # in JSON at position 0
at Object.parse (native)
at createStrictSyntaxError (D:\Code\Deutsch_App_MEAN\node_modules\body-parser\lib\types\json.js:157:10)
at parse (D:\Code\Deutsch_App_MEAN\node_modules\body-parser\lib\types\json.js:83:15)
at D:\Code\Deutsch_App_MEAN\node_modules\body-parser\lib\read.js:121:18
at invokeCallback (D:\Code\Deutsch_App_MEAN\node_modules\raw-body\index.js:224:16)
at done (D:\Code\Deutsch_App_MEAN\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (D:\Code\Deutsch_App_MEAN\node_modules\raw-body\index.js:273:7)
at emitNone (events.js:86:13)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
Ok ... I miserably found the answer ... the username I was passing through MEAN is a string ... Apparently the "post" from the http module does accept only object or array, and not string ... VoilĂ :)
After a http response, I am sending a messing using RabbitMQ (creating a channel etc) and however, the server is complaining that "Error: Can't set headers after they are sent."
Here is the code:
var amqp = require('amqplib');
var when = require('when');
var rabbitmq_conn = amqp.connect('amqp://localhost' );
function push_keystroke_data(session_id, data) {
var QUEUE_NAME = 'hello';
var msg = session_id;
when(rabbitmq_conn.createChannel()).then(function(ch) {
var ok = ch.assertQueue(QUEUE_NAME, {durable: false});
ok.then(function(_qok) {
ch.sendToQueue(QUEUE_NAME, new Buffer(msg));
console.log(" [x] Sent '%s'", msg);
ch.close();
});
}).ensure(function() {
conn.close();
});
}
router.post('/', function(req, res, next) {
// current session id
var sid;
if (req.cookies.papi) {
sid = req.cookies.papi.session_id;
} else {
sid = generate_session_id();
res.cookie('papi', {session_id: sid}, {maxAge: COOKIE_MAX_AGE});
}
res.send(JSON.stringify({ user_id: get_user_id(sid)}));
var data = process_keystroke_data(req.body);
push_keystroke_data(sid, data);
});
I assuming RabbitMQ is setting the headers after the response (I have also tried sending the RabbitMQ message before the response but that also didn't solve anything).
Here is the stack trace:
POST /api 500 220.100 ms - 16
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/response.js:154:12)
at fn (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/response.js:934:10)
at View.exports.renderFile [as engine] (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/jade/lib/index.js:374:12)
at View.render (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/view.js:93:8)
at EventEmitter.app.render (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/application.js:566:10)
at ServerResponse.res.render (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/response.js:938:7)
at /Users/mikeecb/Documents/KeyNA/jsbackend/app.js:62:7
at Layer.handle_error (/Users/mikeecb/Documents/KeyNA/jsbackend/node_modules/express/lib/router/layer.js:58:5)
Any solutions or ideas would be much appreciated.
It turns out that the issue was not that RabbitMQ was setting the headers (which would be weird, why would it be doing any http stuff?) but that after responding res.send(JSON.stringify({ user_id: get_user_id(sid)}));, I tried to send another respond which obviously the issue.
I wrote simple proxy on nodejs and it looks like
var request = require( 'request' );
app.all( '/proxy/*', function( req, res ){
req.pipe( request({
url: config.backendUrl + req.params[0],
qs: req.query,
method: req.method
})).pipe( res );
});
It works fine if remote host is available, but if remote host is unavailable the whole node server crashes with unhandled exception
stream.js:94
throw er; // Unhandled stream error in pipe.
^
Error: connect ECONNREFUSED
at errnoException (net.js:901:11)
at Object.afterConnect [as oncomplete] (net.js:892:19)
How can I handle such errors?
Looking at the docs (https://github.com/mikeal/request) you should be able to do something along the following lines:
You can use the optional callback argument on request, for example:
app.all( '/proxy/*', function( req, res ){
req.pipe( request({
url: config.backendUrl + req.params[0],
qs: req.query,
method: req.method
}, function(error, response, body){
if (error.code === 'ECONNREFUSED'){
console.error('Refused connection');
} else {
throw error;
}
})).pipe( res );
});
Alternatively, you can catch an uncaught exception, with something like the following:
process.on('uncaughtException', function(err){
console.error('uncaughtException: ' + err.message);
console.error(err.stack);
process.exit(1); // exit with error
});
If you catch the uncaught exception for ECONNREFUSED make sure to restart your process. I saw in testing that the socket becomes unstable if you ignore the exception and simply try to re-connect.
Here's a great overview: http://shapeshed.com/uncaught-exceptions-in-node/
I ended up using the "forever" tool to restart my node process, with the following code:
process.on('uncaughtException', function(err){
//Is this our connection refused exception?
if( err.message.indexOf("ECONNREFUSED") > -1 )
{
//Safer to shut down instead of ignoring
//See: http://shapeshed.com/uncaught-exceptions-in-node/
console.error("Waiting for CLI connection to come up. Restarting in 2 second...");
setTimeout(shutdownProcess, 2000);
}
else
{
//This is some other exception..
console.error('uncaughtException: ' + err.message);
console.error(err.stack);
shutdownProcess();
}
});
//Desc: Restarts the process. Since forever is managing this process it's safe to shut down
// it will be restarted. If we ignore exceptions it could lead to unstable behavior.
// Exit and let the forever utility restart everything
function shutdownProcess()
{
process.exit(1); //exit with error
}
You should actually try to prevent the ECONNREFUSED exception from becoming uncaught:
var request = require( 'request' );
app.all( '/proxy/*', function( req, res ){
req.pipe( request({
url: config.backendUrl + req.params[0],
qs: req.query,
method: req.method
}))
.on('error', err => {
const msg = 'Error on connecting to the webservice.';
console.error(msg, err);
res.status(500).send(msg);
})
.pipe( res );
});
If you get an actual uncaught exception, then you should just let the application die.