I'm trying to do a login function but the Error: Can't set headers after they are sent is keep showing up in my terminal, based on my understanding this error is causing by res but I can't find the error in my code..
this is what I have so far:
login function
this.handleLoginRequest = function(req, res, next) {
"use strict";
var data = req.body;
users.validateLogin(data, function(err, user) {
"use strict";
if(err) {
res.send(400, err.message);
}else{
sessions.startSession(data, function(err, session_id) {
"use strict";
if (err) return next(err);
res.cookie('session', session_id);
res.send(200);
});
}
});
}
validateLogin function
this.validateLogin = function(data, callback) {
"use strict";
// Callback to pass to MongoDB that validates a user document
function validateUserDoc(err, user) {
"use strict";
if (err) return callback(err, null);
if (user) {
if (bcrypt.compareSync(data.password, user.password)) {
callback(null, user);
} else {
callback(new Error("Invalid password/email"), null);
}
}else{
// no such user error
callback(new Error("no such user"), null);
}
}
users.findOne({ '_id' : data.email }, validateUserDoc);
process.nextTick(function() {
callback(null, data);
});
}
It looks like the callback passed to validateLogin is called twice:
once from validateUserDoc;
once from the process.nextTick callback (that part seems to be some left-over code);
Related
In a node.js express project I want to implement a switch-to-user function for admin users. The admin can either type the username or the user-id in a box. Below is the code to process this request. The problem is that if the first database call failed, I need to repeat it with another kind of query, before continuing with the login code. How can I add a conditional asynchronous call before the call to req.login?
router.route('/switchuser')
.post(function (req, res) {
mongoose.model('Users').findById(req.body.idOrName, function (err, user) {
if (!user) {
mongoose.model('Users').findOne({ username: req.body.idOrName }, function (err, user_) {
user = user_;
if (err) {
res.status(404);
res.send("User not found " + err);
}
});
}
req.login(user, function (err) {
if (err) {
res.status(404);
res.send("There was a problem switching the user: " + err);
}
else {
res.format({
html: function () {
res.redirect("/");
}
});
}
})
});
});
You are experiencing callback hell. When a user is not found by ID, you start a second callback. The problem is your login as method executes outside of this second callback which means it doesn't wait for the response. Callback hell can quickly cause a lot of duplicate code. One solution is to pull your common bits of code out into functions. Below is one example of how you might do that. Notice that loginAs may be called in two different spots depending on whether the user was found by ID, or required an additional lookup.
router.route('/switchuser')
.post(function (req, res) {
mongoose.model('Users').findById(req.body.idOrName, function (err, user) {
if (err) {
sendErrorResponse(err);
return;
}
if (user) {
loginAs(user, req, res);
return
}
mongoose.model('Users').findOne({ username: req.body.idOrName }, function (err, user) {
if (err) {
sendErrorResponse(err, req, res);
return;
}
if (!user) {
sendNotFoundResponse(req, res);
return;
}
loginAs(user, req, res);
});
});
});
function loginAs(user, req, res) {
req.login(user, function (err) {
if (err) {
res.status(404);
res.send("There was a problem switching the user: " + err);
}
else {
res.format({
html: function () {
res.redirect("/");
}
});
}
})
}
function sendErrorResponse(err, req, res) {
res.status(500);
res.send("Failed to switch user " + err);
}
function sendNotFoundResponse(req, res) {
res.status(404);
res.send("Could not find user");
}
Now depending on what version of Mongoose JS you are using, a limited version of Promises may be available already. Your code could be cleaned up even further like so.
router.route('/switchuser')
.post(function (req, res) {
mongoose.model('Users').findById(req.body.idOrName)
.then(function (user) {
// If the user was found, return it down the chain
if (user) {
return user;
}
// If the user was not found, return the promise for the next query
return mongoose.model('Users').findOne({ username: req.body.idOrName });
})
.then(function(user) {
if (!user) {
sendNotFoundResponse(req, res);
return;
}
loginAs(user, req, res);
})
.catch(function(err) {
sendErrorResponse(err, req, res);
});
});
});
function loginAs(user, req, res) {
req.login(user, function (err) {
if (err) {
res.status(404);
res.send("There was a problem switching the user: " + err);
}
else {
res.format({
html: function () {
res.redirect("/");
}
});
}
})
}
function sendErrorResponse(err, req, res) {
res.status(500);
res.send("Failed to switch user " + err);
}
function sendNotFoundResponse(req, res) {
res.status(404);
res.send("Could not find user");
}
You'll notice with promises you can lump all the errors together at the bottom in a single catch. Cleaner result with less duplicate code and methods calls. You may need to perform an exec to get access to the catch method. Just read the Mongoose docs and you should be fine.
I've the below Node.js application which uses MongoDb:
var MongoClient = require('mongodb').MongoClient;
var demoPerson = { name:'John', lastName:'Smyth' };
var findKey = { name: 'John' };
MongoClient.connect('mongodb://127.0.0.1:27017/demo', { useNewUrlParser: true }, function(err, client) {
const db = client.db('demo');
if(err) throw err;
console.log('Successfully connected');
//console.log(db);
var collection = db.collection('people');
collection.insert(demoPerson, function(err, docs) {
console.log('Inserted', docs[0]);
console.log('ID:', demoPerson._id);
collection.find(findKey).toArray(function(err, results) {
console.log('Found results:', results);
collection.remove(findKey, function(err, results) {
console.log('Deleted person');
db.close();
});
});
});
});
When I run it I get this error:
TypeError: db.close is not a function
I can't understand why this doesn't work. Can anybody help?
As #Neil Lunn commented, client.close() should be used instead of db.close().
How can I add a new document and return the whole collection? For example, I want to add a message and the respose should have all messages added
create: function(req, res) {
var user_id = req.token.sid;
req.allParams().user = user_id;
Message.create(req.allParams(), function(err, message) {
if (err) return res.json(err.status, {err: err});
});
});
You can create a custom route for your action in config/routes.js:
'POST message/create' : { controller: 'MessageController', action: 'create' }
And add some lines to your code in the MessageController.js:
create: function(req, res) {
var user_id = req.token.sid;
req.allParams().user = user_id;
Message.create(req.allParams(), function(err, message) {
if (err) return res.json(err.status, {err: err});
Message.find().exec(function(err, messages){
if (err) return res.json(err.status, {err: err});
return res.json(messages);
})
});
}
it works for me.
I was trying to use two different app.param for different paramaters but it dosnt seem to be possible. when the server checks for the first paramater i get it to sequencially check the second paramater and even if it is checking the second paramater in the second app.param, it nullifies the first paramater from working.
app.param('chatroom', function(req, res, next, chatroom) {
Chatroom.findOne({ 'chat.roomname' : chatroom }, function(err, chatroom) {
if(err)
throw err;
if(!chatroom)
return next();
req.chatroom = chatroom;
next();
});
});
app.get('/:chatroom', function(req, res) {
Profile.findOne({ 'pic.username' : req.user.local.username}, function(err, profilepic) {
if(err)
throw err;
res.render('pages/chatroom', {
chatroom : req.chatroom,
profilepic : profilepic,
user : req.user
});
});
});
app.param('username', function(req, res, next, username) {
User.findOne({ 'local.username' : username }, function(err, user) {
if(err)
throw err;
if(!user){
return res.sendStatus(404);
}
req. profile = user;
next();
});
});
app.get('/:username', function(req, res) {
async.parallel([
function(callback) {
Profile.findOne({ 'pic.username' : req. profile.local.username }, function(err, profilepic) {
if(err)
throw err;
callback(null, profilepic);
});
}, function(callback) {
Buddy.find({ 'bud.username' : req. profile.local.username }, function(err, buddy) {
if(err)
throw err;
callback(null, buddy);
});
}, function(callback) {
Buddy.count({ 'bud.username' : req. profile.local.username}, function(err, buddycount) {
if(err)
throw err;
callback(null, buddycount);
});
}, function(callback){
Userpost.find({ 'post.postuser' : req. profile.local.username }).sort({ 'post.date' : 1 }).exec(function(err, userpost) {
if(err)
throw err;
callback(null, userpost);
});
}, function(callback) {
Userpost.count({ 'post.postuser' : req. profile.local.username }, function(err,postcount) {
if(err)
throw err;
callback(null, postcount);
});
}], function(err, results){
if(err)
throw err;
var profilepic = results[0];
var buddy = results[1];
var buddycount = results[2];
var userpost = results[3];
var postcount = results[4];
res.render('pages/visitprofile', {
user : req. profile,
profilepic : profilepic,
buddy : buddy,
buddycount : buddycount,
userpost : userpost,
postcount : postcount
});
}
);
});
So is there I way i can get both parameters to work on the server at the same time. I reorder them and one works with the other one not working. Maybe i can make next() function skip over the other parameter some how?
I cannot seem to get the last callback (commented as "optional callback") called to send the result back to the browser. Any pointers as to what I am doing wrong? I am using the following modules: async, restify and postgresql for node.js
console.log('Start');
var async = require('async');
var restify = require('restify');
var server = restify.createServer();
server.use(restify.bodyParser());
server.get('/user/creationdate/:username', function(req, res, next) {
var userName = req.params.username;
var record;
async.parallel([
function(callback){
getUserByName(userName, function(err, user) {
if (err) return callback(err);
record = user;
});
}
],
// optional callback
function(err){
console.log('5. Following record has been retrieved:' + record);
res.send(record);
});
next();
});
server.listen(8080, function () {
console.log('%s listening at %s', server.name, server.url);
});
handleError = function handleError(err) {
if(!err) { return false; }
else {
console.log('The following error occurred:' + err);
}
return true;
};
function getPgClient(){
var pg = require('pg');
var client = new pg.Client({
user: 'postgres',
password: 'password',
database: 'foobar',
host: '192.168.1.100',
port: 5432
});
client.on('drain', client.end.bind(client)); //disconnect client when all queries are finished
return client;
}
function getUserByName(userName, callback){
var client = getPgClient();
console.log('2. Trying to connect to DB');
client.connect(function(err) {
console.log('3. Error connecting to DB:' + handleError(err));
if(handleError(err)) return callback(err);
client.query("SELECT created_at FROM users WHERE username='" + userName + "'", function(err, result) {
if(handleError(err)) return;
console.log('4. Error occurred:' + err);
console.log(result);
console.log(callback);
callback(null, result);
})
});
}
I'm not sure why you're using async since you're only calling one asynchronous function. But the reason your callback isn't called is because you're not ending the first function by calling its callback:
async.parallel([
function(callback) {
getUserByName(userName, function(err, user) {
// call async callback with err and user
callback(err, user);
});
}
], function(err, record) {
console.log('5. Following record has been retrieved:' + record);
res.send(record);
});
Or, shorter:
async.parallel([
function(callback) {
getUserByName(callback);
}
], function(err, record) {
console.log('5. Following record has been retrieved:' + record);
res.send(record);
});
Or, in this case, even shorter (and without the need for async):
getUserByName(function(err, record) {
// handle error, or ...
console.log('5. Following record has been retrieved:' + record);
res.send(record);
});
Saw my mistake, missing the line where I should have returned the callback as in:
async.parallel([
function(callback){
getUserByName(userName, function(err, user) {
if (err) return callback(err);
record = user;
return callback(null, record);
});
}