Nodejs/Express - Error: Can't set headers after they are sent - javascript

Pretty new to node/express. I'm checking to see if the user (via the username) already exists in the database that one wants to register to, giving an error if they do already exist.
When I use curl to try to set it off intentionally, I get the following error:
Error: Can't set headers after they are sent.
I know already that the first check I do to ensure that all the fields are filled in works correctly, and provides no issues with headers being set multiple times.
Any help would be greatly appreciated.
(My relevant code is below. If you need anything else, feel free to say so!)
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
});
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});

When you are returning inside User.count and user.save, you are returning only from inside the callbacks but not the entire method.
Its a good practice to send a response in just one place. At the end of the method. Before that evaluate your conditions and set the response code and response message in some variable. Which you can use to send the response as a final step.
Try this as a workaround for now:
router.post('/register', function(req, res, next)
{
if(!req.body.username || !req.body.password)
{
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count)
{
console.log(count);
if(count > 0)
{
return res.status(400).json({message: 'This user already exists!' });
}
else
{
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err)
{
if(err)
{
return next(err);
}
return res.json({ token: user.generateJWT()});
});
}
});
});

Put all your code in the callback function of User.count, otherwise the two part of code are executed
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});
});

Related

Cannot set headers after they are sent to the client error

I have a login form that authenticates using postgresql I'm trying to check if users exists then redirect the client to the other page. The code is:
app.post('/login', (req, res) => {
var Enteredusername = req.body.username;
var Enteredpassword = req.body.password;
pool.query("SELECT * FROM tbl_users WHERE username = $1 AND password = $2", [Enteredusername, Enteredpassword], (err, result) => {
if (err) return console.log('error in query', err);
// need to check if user exists
let user = (result.rows.length > 0) ? result.rows[0] : null;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}
res.redirect('/posts')
});
});
And I got the error:
_http_outgoing.js:470
throw new ERR_HTTP_HEADERS_SENT('set');
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
How Can I fix it?
It's the async behavior of javascript res.redirect('/posts') is executed before req.flash and res.render you can hack it like this :
req.session.userId = Enteredusername;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}else{
return res.redirect('/posts')
}

Combine two functions into one Stripe and Passport

I currently have it set so a user signs up with just their email and password. Simple and easy, I want them to be able to select a plan from a select element. I know how to access the part of the request I want using req.body.plan if the name for the select is plan.
I have two separate controller functions currently to do this in a bit different way. I have it so the postSignup signs the user up using passport.js and everything is all good. I also have postPlan that when the user is on /billing and selects a plan then submits the form that they have that plan assigned to them. The part I am stuck on is the User.findById(req.user.id, function(err, user) { part, more specifically the req.user.id. How can I take the two functions below and have them combined so that is sets the user's plan based off the select element in the signup form.
postPlan
exports.postPlan = function(req, res, next){
var plan = req.body.plan;
console.log("plan: ",req.body.plan);
var stripeToken = null;
if(plan){
plan = plan.toLowerCase();
}
if(req.user.stripe.plan == plan){
req.flash('info', {msg: 'The selected plan is the same as the current plan.'});
return res.redirect(req.redirect.success);
}
if(req.body.stripeToken){
stripeToken = req.body.stripeToken;
}
if(!req.user.stripe.last4 && !req.body.stripeToken){
req.flash('errors', {msg: 'Please add a card to your account before choosing a plan.'});
return res.redirect(req.redirect.failure);
}
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.setPlan(plan, stripeToken, function (err) {
var msg;
if (err) {
if(err.code && err.code == 'card_declined'){
msg = 'Your card was declined. Please provide a valid card.';
} else if(err && err.message) {
msg = err.message;
} else {
msg = 'An unexpected error occurred.';
}
req.flash('errors', { msg: msg});
return res.redirect(req.redirect.failure);
}
req.flash('success', { msg: 'Plan has been updated.' });
res.redirect(req.redirect.success);
});
});
};
postSignup
exports.postSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
// calls next middleware to authenticate with passport
passport.authenticate('signup', {
successRedirect: '/dashboard', // Select redirect for post signup
failureRedirect: '/signup',
failureFlash : true
})(req, res, next);
next();
};
Current Idea
exports.postSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
// calls next middleware to authenticate with passport
passport.authenticate('signup', {
successRedirect: '/dashboard', // Select redirect for post signup
failureRedirect: '/signup',
failureFlash : true
});
// var plan = req.body.plan;
var plan = 'silver';
var stripeToken = null;
if(req.body.stripeToken){
stripeToken = req.body.stripeToken;
}
User.findById({"email":req.body.email}, function(err, user) {
if (err) return next(err);
console.log("Ran here");
user.setPlan(plan, stripeToken, function (err) {
var msg;
if (err) {
if(err && err.message) {
msg = err.message;
} else {
msg = 'An unexpected error occurred.';
}
req.flash('errors', { msg: msg});
return res.redirect(req.redirect.failure);
}
req.flash('success', { msg: 'Thanks for signing up! ' });
res.redirect(req.redirect.success);
});
});
(req, res, next);
next();
};
Error message for current attempt above
{
"message": "Cast to ObjectId failed for value \"{ email: 'snappierjaguar#gmail.com' }\" at path \"_id\" for model \"User\"",
"error": {
"message": "Cast to ObjectId failed for value \"{ email: 'snappierjaguar#gmail.com' }\" at path \"_id\" for model \"User\"",
"name": "CastError",
"stringValue": "\"{ email: 'snappierjaguar#gmail.com' }\"",
"kind": "ObjectId",
"value": {
"email": "snappierjaguar#gmail.com"
},
"path": "_id"
}
}

exit block of code with express and node

Here is my code
// on all routes that end with "users", do the following
router.route('/users')
.post(function(req, res, next) {
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
User.find({username : user.username}, function(err, results){
if (results.length > 0) {
//if (err) res.send(err);
console.log('User exists: ', user.username);
res.send('User exists');
next();
}
});
user.save(function(err) {
if (err)
res.send(err);
res.json({
message: 'Created user.',
username: req.body.username,
password: req.body.password
});
});
})
User is just a Mongoose schema.
If the username is found in that first callback, I want to send a basic response of "User Exists", then exit. Right now I get an error because it moves on to the user.save bit and tries to write more info to the response which has already ended.
How can I exit the User.find block and the .post block altogether? In a normal C-like language I'd simply do return; but doing so only exits just the User.find block.
Thanks for any help
Try to write another case in else condition and return the callback like this
router.route('/users')
.post(function(req, res, next) {
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
User.find({username : user.username}, function(err, results){
if (results.length > 0) {
//if (err) res.send(err);
console.log('User exists: ', user.username);
res.send('User exists');
return next();
}else{
user.save(function(err) {
if (err)
return res.send(err);
return res.json({
message: 'Created user.',
username: req.body.username,
password: req.body.password
});
});
}
});
})
In your current implementation, if it finds the user, it will run whatever you have written in your callback. You happen to have next() inside your callback. next() will exit this route and find the next route that would match. That means whatever you wrote under, won't be executed (I'm talking about the user.save part.
Anyways, to answer your question, if you want it to exit the User.find block and the .post all together, then just put the next() method below and outside your if statement.
I hope this helps.

Unexpected results with MongooseJS findOne()

Using Mongoose as an ODM with NodeJS, but not fully understanding how the error handling works. It works, but doesn't look right, and isn't in line with the documentation, so I'm worried that going down this road will haunt me later on.
For example, here is a basic signin route:
app.post('/signin', function(req, res){
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function(err, user){
if (err){
console.log('Database Error')
return res.json({error: 'Database Error'})
} else {
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'})
} else {
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
})
}
}
})
})
I'm especially worried about:
if (err) {
//do something
} else {
if (!user){
//do something else
} else {
//log the user in
}
}
Haven't really used Mongo before today, but this feels like a lot of conditional error handling. Is there something that I'm not understanding properly here?
Was going to post as a comment but it was easier to paste this as an answer..
You can simplify the if-else nesting since you are returning at the end of each conditional, like so:
app.post('/signin', function (req, res) {
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function (err, user) {
if (err) {
console.log('Database Error');
return res.json({error: 'Database Error'});
}
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'});
}
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
});
});
});

Error: Can't set headers after they are sent in Node / Express

I'm doing a tutorial in the MEAN Machine book, chapter about route APIs.
Full code posted here: https://gist.github.com/leongaban/6db44e513db4ca9e784f
The following code is the API to get all users, and then to get a user by a certain id.
Get all users:
// api/users
apiRouter.route('/users')
// create a user (accessed at POST http://localhost:8615/api/users)
.post(function(req, res) {
// create a new instance of the User model
var user = new User();
// set the users information (comes from the request)
user.name = req.body.name;
user.username = req.body.username;
user.password = req.body.password;
// save the user and check for errors
user.save(function(err) {
if (err) {
// duplicate entry
if (err.code == 11000)
return res.json({ success: false, message: 'A user with that username already exists. '});
else
return res.send(err);
}
// return a message
res.json({ message: 'User created!' });
});
})
// get all users (access at GET http://localhost:8615/api/users)
.get(function(req, res) {
User.find(function(err, users) {
if (err) return res.send(err);
// return the users
res.json(users);
})
});
Follow the above is the code to get just 1 user based on id:
// api/users/:user_id
apiRouter.route('/users/:user_id')
// get the user with that id
// (accessed at GET http://localhost:8615/api/users/:user_id)
.get(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
if (err) return res.send(err);
// return that user
res.json(user);
})
// update the user with this id
.put(function(req, res) {
// use our user model to find the user we want
User.findById(req.params.user_id, function (err, user) {
if (err) return res.send(err);
// update the users info only if its new
if (req.body.name) user.name = req.body.name;
if (req.body.username) user.username = req.body.username;
if (req.body.password) user.password = req.body.password;
// save the user
user.save(function(err) {
if (err) return res.send(err);
// return a message
res.json({ message: 'User updated!' });
});
})
})
// delete the user with this id
.delete(function(req, res) {
User.remove({
_id: req.params.user_id
}, function(err, user) {
if (err) return res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
});
Now going to my localhost:8615/api/users/ I am able to get all the users back
I then select an id from one of the users, for example:
localhost:8615/api/users/54b64c770dedef7c1a7d2c8b
And I get the following error:
TypeError: Object #<Query> has no method 'put'
at adminRouter.param.req.name (/Users/leongaban/NodeDallas/projects/awesome- test/server.js:110:3)
at Layer.handle [as handle_request] (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/layer.js:82:5)
at next (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/route.js:100:13)
at Route.dispatch (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/route.js:81:3)
at Layer.handle [as handle_request] (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/layer.js:82:5)
at /Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/index.js:233:24
at param (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/index.js:330:14)
at param (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/index.js:346:14)
at Function.proto.process_params (/Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/index.js:390:3)
at /Users/leongaban/NodeDallas/projects/awesome-test/node_modules/express/lib/router/index.js:227:12
In my Gist you can see that my headers are set once at the top:
https://gist.github.com/leongaban/6db44e513db4ca9e784f
Any idea why I'm having this problem?
You are missing the closing }) for the .get() method. So put is being called on the return of User.findById() It should be
.get(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
if (err) return res.send(err);
// return that user
res.json(user);
})
}) // <-- was missing
// update the user with this id
.put(function(req, res) {

Categories