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?
Related
So i made a reset password function and it looks like this:
router.get('/reset/:id',function(req,res,next){
User.findOne({'resetToken': req.params.id.trim()})
.exec(function(error, user){
if (error) return next(error);
else if(!user){
var err = new Error('Invalid Reset Link! The system is unable to find a user with the given reset code.');
err.status = 401;
return next(err);
}else return res.render('resetpass',{token: req.params.id});
})
})
router.post('/reset/:id', function(req,res,next){
if(req.body.newPassword === req.body.confirmNewPassword)
User.findOne({'resetToken': req.params.id.trim()},function(error,user){
if(error) return next(error);
bcrypt.hash(req.body.password, 5, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
user.resetToken ='';
user.save();
})
});
return res.redirect('/login');
})
so when i complete the reset password fields and click submit i get this in console : Cannot set headers after they are sent to the client
and if i use this:
router.post('/reset/:id', function(req,res,next){
if(req.body.newPassword === req.body.confirmNewPassword)
var cp;
User.findOne({'resetToken': req.params.id.trim()},function(error,user){
if(error) return next(error);
bcrypt.hash(req.body.password, 5, function(err, hash) {
if (err) {
return next(err);
}
cp = hash;
})
user.password = cp;
user.resetToken ='';
user.save();
});
return res.redirect('/login');
})
my user.password is empty and i get this : Cannot set property 'password' of null -> becouse i set that in UserSchema.
So why is that ? i tried with console.logs in other function just with variables and is something like that :
bcrypt.hash(.... {console.log('a') })
console.log('b');
my console looks like this :
b
a
So bcrypt console.log is after. What can i do ?
Please note that User.findOne() is an async operation.
The redirection res.redirect('/login') should be called after new password is saved.
You could try this code below:
router.post('/reset/:id', function (req, res, next) {
if (req.body.newPassword === req.body.confirmNewPassword) {
User.findOne({
'resetToken': req.params.id.trim()
}, function (error, user) {
if (error) return next(error);
bcrypt.hash(req.body.password, 5, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
user.resetToken = '';
user.save();
// Do the redirection here
res.redirect('/login');
})
});
}
// Do nothing here
})
I've been struggling last week with error handling, especially error handling in Node.js modules. So first, here is my code:
user.js route
router.post('/register', function(req, res) {
var newUser = new User({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: req.body.password,
});
User.addUser(newUser, function(err, user) {
if(err) {
return next(err)
} else if(user) {
res.status(403).send('User already exists');
} else {
res.sendStatus(200);
}
});
});
user.js module
module.exports.addUser = function(newUser, cb) {
User.findOne({ email: newUser.email }, function(err, user) {
if(err) {
cb(err);
} else if(user) {
cb(null, user);
} else {
bcrypt.genSalt(10, function(err, salt) {
if(err) {
cb(err);
} else {
bcrypt.hash(newUser.password, salt, function(err, hash) {
if(err) {
cb(err)
} else {
newUser.password = hash;
newUser.save(function(err, newUser) {
if(err) {
cb(err);
} else {
cb(null, false);
}
});
}
});
}
});
}
});
}
Everytime if there is error inside user.js module, call callback function and handle error inside user.js route. This works, but that mess inside my module doesn't look good, because there is so many if-else statements..
Is there better approach, or do I have to check everytime if there is error?
You could simplify your code to something like:
module.exports.addUser = function(newUser, cb) {
User.findOne({ email: newUser.email }, function(err, user) {
if(err) {
cb(err);
return;
}
if(user) {
cb(null, user);
return ;
}
bcrypt.genSalt(10, function(err, salt) {
if(err) {
cb(err);
return;
}
bcrypt.hash(newUser.password, salt, function(err, hash) {
if(err) {
cb(err)
return;
}
newUser.password = hash;
newUser.save(function(err, newUser) {
if(err) {
cb(err);
return;
}
cb(null, false);
});
});
});
});
}
However, if I were you and as #Scimonster stated in his comment, this is a typical use case for promises that would allow you to write more readable code and avoid the callback hell
I am using mlab and mongojs along with Angular. When I attempt to toggle a boolean value (onStatus), the button in the view toggles from off to on, but it crashes the app. When I check in the DB the property has been removed from the document. Code snippets below:
device.service.ts
toggleDevice(updatedStatus){
var headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.put('/api/device/'+updatedStatus._id, JSON.stringify(updatedStatus), {headers: headers})
.map(res => res.json());
}
devices.component.ts
toggleDevice(device){
var currentStatus = device.onStatus;
var updatedStatus = {
_id: device._id,
name: device.name,
onStatus: !currentStatus
};
this.deviceService.toggleDevice(updatedStatus)
.subscribe(data => {
device.onStatus = !device.onStatus
});
}
devices.compoonents.html
<button class="btn" (click)="toggleDevice(device)" type="button">{{ device.onStatus ? 'Switch Off' : 'Switch On' }}</button>
API routing
var express = require('express');
var router = express.Router();
var database = require('../config/database');
var mongojs = require('mongojs');
var db = mongojs(database.url, ['devices'])
// GET : All devices
router.get('/devices', function(req, res, next){
db.devices.find(function(err, devices){
if(err) {
res.send(err);
}
res.json(devices);
});
});
// GET : Single device
router.get('/device/:id', function(req, res, next){
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
// POST : Save a device
router.post('/device', function(req, res, next){
var device = req.body;
device.onStatus = false;
if(!device.name) {
res.status(400);
res.json({
"error": "Please add a name."
});
} else {
db.devices.save(device, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
}
});
// DELETE : A device
router.delete('/device/:id', function(req, res, next){
db.devices.remove({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
// PUT : Update a device
router.put('/device/:id', function(req, res, next){
var device = req.body;
var updatedDevice = {};
if(device.name) {
updatedDevice.name = device.name;
}
if(!updatedDevice){
res.status(400);
res.json({'Error': 'Name not specified'});
} else {
db.devices.update({_id: mongojs.ObjectId(req.params.id)}, updatedDevice, {}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
}
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
}
res.json(device);
});
});
module.exports = router;
Errors
/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
at ServerResponse.header (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:719:10)
at ServerResponse.send (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:164:12)
at ServerResponse.json (/home/chopin/Development/homeautomation/node_modules/express/lib/response.js:250:15)
at /home/chopin/Development/homeautomation/routes/devices.js:80:9
at /home/chopin/Development/homeautomation/node_modules/mongojs/lib/collection.js:50:5
at handleCallback (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/utils.js:95:56)
at /home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/lib/cursor.js:674:5
at handleCallback (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/node_modules/mongodb-core/lib/cursor.js:171:5)
at nextFunction (/home/chopin/Development/homeautomation/node_modules/mongojs/node_modules/mongodb/node_modules/mongodb-core/lib/cursor.js:682:5)
[nodemon] app crashed - waiting for file changes before starting...
Thanks for any help.
EDIT: I should mention the error only occurs the 2nd time I toggle the button. I am assuming as this has been removed from DB for some reason. The name of and ID persist, yet the onStatus doesn't.
EDIT 2: Full code https://github.com/Sacki2013/homeAutomation
You are trying send the response even after it is already sent. All you have to do is that add return statements after your response is sent.
var express = require('express');
var router = express.Router();
var database = require('../config/database');
var mongojs = require('mongojs');
var db = mongojs(database.url, ['devices'])
// GET : All devices
router.get('/devices', function(req, res, next){
db.devices.find(function(err, devices){
if(err) {
res.send(err);
return;
}
res.json(devices);
});
});
// GET : Single device
router.get('/device/:id', function(req, res, next){
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
// POST : Save a device
router.post('/device', function(req, res, next){
var device = req.body;
device.onStatus = false;
if(!device.name) {
res.status(400);
res.json({"error": "Please add a name."});
} else {
db.devices.save(device, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
}
});
// DELETE : A device
router.delete('/device/:id', function(req, res, next){
db.devices.remove({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
// PUT : Update a device
router.put('/device/:id', function(req, res, next){
var device = req.body;
var updatedDevice = {};
if(device.name) {
updatedDevice.name = device.name;
}
if(!updatedDevice){
res.status(400);
res.json({'Error': 'Name not specified'});
} else {
db.devices.update({_id: mongojs.ObjectId(req.params.id)}, updatedDevice, {}, function(err, device){
if(err) {
res.send(err);
return;
}
/*
* Commenting following line because
* you are sending a response in `findOne`
*/
// res.json(device);
db.devices.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, device){
if(err) {
res.send(err);
return;
}
res.json(device);
});
});
}
});
module.exports = router;
On your /device/:id PUT endpoint you're doing the update and findOne calls asynchronously, independent from each other, so res.json() is called twice. Try moving your findOne function inside the update callback.
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'm trying to send some form data, but I get this error using express.js:
Can't set headers after they are sent.
This is my code so far:
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.send(200);
});
console.log(res.send(user)) //HERE IS WHERE I GET THE ERROR
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});
By console logging res.send(user) you are sending again. You can send once and once only.
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.status(200).send(user);
});
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});