I have a node.js application with Express for routing and Mongoose for the database access.
I have this HTTP DELETE request that is deleting an entry in the database if certain requirements are met.
.delete(function (req, res) {
Movie.findOne({_id: req.params.id
}, function (err, movie) {
if (err)
res.send(err);
for (var i = 0, len = movie.actors.length; i < len; i++) {
if (movie.actors[i].actor == "Chuck Norris"){
res.status(403).send({ message: 'Cannot delete Chuck Norris' });
}
else {
Movie.remove({_id: req.params.id
}, function (err, movie) {
if (err)
res.send(err);
res.json({message: 'Movie deleted'});
});
}
}
});
});
If I send a HTTP DELETE with the ID of a film, it will check in the actors list if Chuck Norris is in the movie and I will prevent the deletion if he is there.
The problem is that my console is returning me this error :
Error: Can't set headers after they are sent.
So I presume that this an issue with my callbacks. Due to the asynchronus nature of node.js the "slow" database call made that my .delete sent the headers before the findOne finished ?
How can I manage to validate before deletion and send a proper http error code if the deletion is not possible ?
Be careful when you respond to a request multiple times in the same scope like that. On error you should prepend return to res.send(err) so that execution will not continue further. The same goes for your res.status() in the for-loop.
Related
I get this error #Can't set headers after they are sent# when using dbquery multiple times.
Hi, I'm almost new to node.js and I can't figure out why this error appears. If I try with one dbquery then everything is ok. But if I use multiple query then it crashes.
router.get(
'/auth/facebook/token',
(req, res, next) => {
passport.authenticate('facebook-token', (error, user, info) => {
if (user){
myDB.setUser(user.email,(err,pinfo) =>{
if (err) {
res.send(err);
}
else {
res.send(pinfo); <- Crash at this line!!!
}
});
}
next();
})(req,res, next);
}
);
function setUser (email, cb) {
pool.query("INSERT INTO users (email,pinfo) VALUES (?,0)", email, (err, result) => {
if(err){
cb(err);
return;
}
else{
pool.query("SELECT pinfo FROM users WHERE email = ?", email,(err,pinfo) =>{
cb(err,pinfo);
});
}
});
}
You are calling next middle layer function using next(). After sending the response to the user. Try without next or modify your logic to. Hope this will help you
once you are used res.send you cannot use it again
it seems you are sending a response at two positions please check the logic thoroughly
Some Where you sending response twice and hence this error. R.sarkar is right take out next() call and add it somewhere you will want to continue with your next dbquery or else respond from top function only once and never call next().
In my react project I have created a canvas that multiple users can update. I am using react, express and mongoose. When I want to clear the canvas I have a function clearDB() in the App.js component of my front end.
clearDB()
{
alert('Clearing Database')
return fetch(reseturl, {method: 'DELETE'})
.then(res => res.json())
.then(res => {
alert('Deleted:', res.message)
return res
})
.catch(err => console.error(err))
}
this is called by an event listener initiated in componentDidMount when the user presses 'z';
In the express server I have.
app.delete("/reset", function(req, res) {
console.log("delete request sent ");
db.dropCollection("dots", function (err, delOK) {
if (err) {
console.log("error delete collection");
return res;
} if (delOK) {
console.log("delete collection success");
return res;
}
return res
});
return res
});
The problem is that the collection is cleared immediately on pressing 'z', showing as an HTTP OPTIONS on the server output, and the again as a DELETE about a minute later.
This is the first output which appears as soon as the event listener picks up the keystroke.
OPTIONS /reset 204 0.320 ms - 0
delete request sent undefined
delete collection success<--(the log from the clearDB function)
GET /message 200 2.022 ms
Then about 2 minutes later
POST /message 200 2.293 ms - 16
DELETE /reset - - ms - -
delete request sent undefined
delete collection success<--(the log from the clearDB function, again!)
POST /message 200 372.026 ms - 16
The canvas gets cleared twice.Please help. No one else seems to have this problem!
The OPTIONS is only a pre-flight call that is sent to the back end to determine if your request is structured properly based on the back end. You can read more about the OPTIONS call here.
The DELETE call is what actually causes the code in your server's route handler to run.
So, if only one DELETE call is being sent for a single event, then that part is functioning correctly.
If, however, you still have an issue, it must be somewhere else in the code that you haven't posted.
Ending the request with res.end() has stopped the unexpected behaviour.
`app.delete("/reset", function(req, res) { console.log("delete request sent ");
db.dropCollection("dots", function (err, delOK) {
if (err) { console.log("error delete collection");
return res; }
if (delOK) { console.log("delete collection success");
return res; }
res.end();});
return res }); `
I would like to get some help with the following problem. I'm writing my bsc thesis, and this small part of code would be responsible for registering a user. (I'm new at nodejs actually). I'm using express and mongoose for this too.
I would like to process the request data, and check for some errors, first I would like to check if all fields exist, secondly if someone already registered with this e-mail address.
Based on the errors (or on success), I would like to send different responses. If a field is missing, then a 400 Bad request, if a user exists, then 409 Conflict, and 200 OK, if everything is ok. But I would only like to do the callback if there are no errors, but I'm kinda stuck here... I get the error Can't set headers after they are sent, which is obvious actually, because JS continues processing the code even if a response is set.
app.post('/register', function (req, res) {
var user = new User(req.body);
checkErrors(req, res, user, registerUser);
});
var registerUser = function(req, res, user){
user.save(function(err, user){
if (err) return console.log(err);
});
res.sendStatus(200);
};
var checkErrors = function(req, res, user, callback){
var properties = [ 'firstName', 'lastName', 'email', 'password', 'dateOfBirth' ];
for(var i = 0; i < properties.length; i++){
if(!req.body.hasOwnProperty(properties[i])){
res.status(400).send('field ' + properties[i] + ' not found');
}
}
var criteria = {
email: req.body.email
};
User.find(criteria).exec(function(err, user){
if(user.length > 0){
res.status(409).send('user already exists');
}
});
callback(req, res, user);
};
I think the problem is in the for loop in checkErrors. Since you call res.status(400).send() within the loop, you can end up calling it multiple times, which will trigger an error after the first call since a response will already have been sent back to the client.
Inside the loop, you can instead add missing fields to an array, then check the length of the array to see if you should respond with a 400 or continue. That way, you will only call res.status(400).send() one time.
For example:
...
var missingFields = [];
for(var i = 0; i < properties.length; i++){
if(!req.body.hasOwnProperty(properties[i])){
missingFields.push(properties[i]);
}
}
if(missingFields.length > 0) {
return res.status(400).send({"missingFields" : missingFields});
}
...
In general, I advise that you put return in front of each res.send() call, to ensure that no others are accidentally called later on.
An example of this is:
User.find(criteria).exec(function(err, user){
// We put return here in case you later add conditionals that are not
// mutually exclusive, since execution will continue past the
// res.status() call without return
if(user.length > 0){
return res.status(409).send('user already exists');
}
// Note that we also put this function call within the block of the
// User.find() callback, since it should not execute until
// User.find() completes and we can check for existing users.
return callback(req, res, user);
});
You probably noticed that I moved callback(req, res, user). If we leave callback(req, res, user) outside the body of the User.find() callback, it is possible that it will be executed before User.find() is completed. This is one of the gotchas of asynchronous programming with Node.js. Callback functions signal when a task is completed, so execution can be done "out of order" in relation to your source code if you don't wrap operations that you want to be sequential within callbacks.
On a side note, in the function registerUser, if user.save fails then the client will never know, since the function sends a 200 status code for any request. This happens for the same reason I mentioned above: because res.sendStatus(200) is not wrapped inside the user.save callback function, it may run before the save operation has completed. If an error occurs during a save, you should tell the client, probably with a 500 status code. For example:
var registerUser = function(req, res, user){
user.save(function(err, user){
if (err) {
console.error(err);
return res.status(500).send(err);
}
return res.sendStatus(201);
});
};
Your call to registerUser() is defined after the route and would be undefined since it's not a hoisted function.
Your use of scope in the closures isn't correct. For your specific error, it's because you're running res.send() in a loop when it's only supposed to be called once per request (hence already sent headers a.k.a. response already sent). You should be returning from the function directly after calling res.send() as well.
Im trying to get a response from a simple route to check if a client exist in my database... is really simple but when the GET request is fired, it's not resolved and never show any response.
This is the code for the route:
router.get('/clientes/:client_id', function (req, res) {
Cliente.find({ _id: req.params.client_id }, function(err, cliente) {
if(!cliente) {
res.status(404).json({ statusMsg: 'El cliente no existe' });
} else if(err) {
res.json({ error: err });
} else {
res.status(200).json({ cliente: cliente, statusMsg: 'Ok' });
}
});
}); // end: router.get clientes
Pretty simple, but when i run curl http://localhost:8080/omi/v1/clientes/2345 (the id is arbitrary, is just one test to get a 404 response) for a simple get request, i get this in my server logger (morgan): GET /omi/v1/clientes/10 - - ms - -, Because i need to cancel the job.
So i read one, two, three times the code, searched on google and don't get the problem. Anyway my english is not really good too, so maybe i dont make a complete search.
Hi i am developing nodejs application. I am inserting data to mongodb but my page always in 'loading' mode. But strange thing is my data inserted to mongodb immediately but page load not stopping. My code is shown below:
app.post('/Management/Post/New',function(req, res){
new Post({
title:req.body.post.title,
body:req.body.post.body,
keywords:req.body.post.keywords
}).save(function (err, docs){
if(err) {
return res.render(__dirname + "/views/createpost", {
title: 'Yeni Gönderi Oluştur',
stylesheet: 'postcreate',
error: 'Gönderi oluşturulurken bir hata ile karşılaşıldı'
});
}
console.log('Gönderi oluşturuldu');
});
});
Have no idea.
You only send a response when there is an error. If there's no error, you server never sends anything back: that's why the page seems to always be loading.
You need to send a response when you have no error, like this:
.save(function (err, docs){
if(err) { // Executed when there was an error with Mongo
return res.render(...);
} else { // Executed when everything is fine
return res.render(...);
}
});
You aren't handling the success scenario except for a console.log. You need a res.render() or res.redirect() on success, not just error