I have the issue that my next(err) is not working. It ignores error and just loads the page instead of sending HTTP status code 404.
The ldap search works fine and result looks as expected. It just doesn't return error when the else statement is hit.
The console does show the failed in log
app.use(function(req, res, next){
conn.search('dc=foo', opts, function (err, res) {
assert.ifError(err)
var entries = []
res.on('searchEntry', function (entry) {
entries.push(entry.object)
})
res.on('end', function (result) {
conn.unbind(function (err) {
console.log('Disconnecting')
if (entries.length == 1) {
next()
} else {
console.log('fail')
var err = new Error('Permission Denied')
err.status = 404
next(err)
}
})
})
})
})
I don't think next works like that. If you do not want your next middleware to be called, don't call next(error) or next() at all. Use instead :
if (entries.length == 1) {
next()
} else {
console.log('fail')
return res.status(404).send('Permission Denied')
}
Related
I get error: "UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client",
guessing that the problem is with promises, but I don't understand how to fix it.
How do I fix my code to avoid this error, but keep the logic and work with the database?
router.post("/addNote", (req, res) => {
let currentTime = new Date();
currentTime.setUTCHours(currentTime.getUTCHours() + 3);
const post = new PostModel({
title: req.body.inputHeader,
text: req.body.inputText,
author: req.body.author,
createdAt: currentTime
});
post.save().then(() => {
res.json({status: "saved"});
})});
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err)
res.json(err);
res.json(notes);
});
});
router.delete("/deleteNote/:id", (req, res) => {
PostModel.deleteOne(
{
_id: req.params.id
}
).then((notes) => {
if (notes)
res.json({status: "deleted"});
res.json({status: "error while deleting"});
});
});
router.put("/updateNote/:id", (req, res) => {
PostModel.findByIdAndUpdate(
req.params.id,
{
$set: req.body
},
err => {
if (err)
res.send(err);
res.send({status: "updated"})
}
).then((notes) => {
if (notes)
res.json({status: "update"});
res.json({status: "error while updating"});
});
});
router.get("/getNote", (req, res) => {
PostModel.findOne({ _id: req.params.id}).then(post => {
if (!post){
res.send({error: "not found"});
} else {
res.json(post)
}
});
});
router.post("/authorize", (req, res) => {
// bcrypt.hash ("", saltRounds, (err, hash) => {
// console.log(hash);
// });
let resultAuthorization = false;
if (req.body.login === authorization.login) {
resultAuthorization = bcrypt.compareSync(req.body.password, authorization.password);
}
if (resultAuthorization)
res.json({statusAuthorization: "correct"});
res.json({statusAuthorization: "incorrect"});
});
module.exports = router;
The problem is that you are calling res.json several times in one handler. When calling it a second time a response has already been sent so you can not send another response.
As tkausl already pointed out you are missing elses so that res.json is being called once.
You need to change your handlers similar to the /getNote handler.
The handler for the endpoint deleteNode/:id for example has to be changed to this:
router.delete("/deleteNote/:id", (req, res) => {
PostModel.deleteOne(
{
_id: req.params.id
}
).then((notes) => {
if (notes)
res.json({status: "deleted"});
else
res.json({status: "error while deleting"});
});
});
This else also needs to be added in /getNotes and /authorize.
The reason is you're trying to send a response more than once. Once the response is returned, if the program sends a response again, this error occurs.
The reason for the problem is that you do not return the current function after the if condition.
Let me explain with some codes
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err) {
res.json(err);
console.log('We encountered an error and sent the error as a response. But our function still continue...');
}
res.json(notes);
console.log('We tried to sent successfull response but function still continue');
});
});
So after the response, you should end the function or make sure that you do not call any other response function in the ongoing code stream/flow.
Lets fix your code.
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err) {
return res.json(err);
// It is not will be continued because the function returned with response.
}
return res.json(notes);
console.log('No console output')// It is will not be called because function returned.
});
});
I´m trying to create a function that make a request to a website and, as the return of the function, I want to deliver the object "BODY" created by the request. But everytime I have "Object undefined" return.
What I need to change in order to deliver this result ?
Here are the code:
// ---- Funções --------------------------------------------------------------------------------//
function consultaCep(cep){
request(`https://viacep.com.br/ws/${cep}/json/`, {json: true}, (err, res2, body) => {
if(err) return console.log(`Erro na consulta: ${err}`)
return body;
});
}
// Rotas ------------------------------------------------------------------------------------//
app.get('/', (req,res)=> {
x = consultaCep('13085485');
console.log(x);
res.end(x);
Thanks a lot.
There are many ways to achieve this. You can use a callback.
You can pass a callback to the method that invokes the web request, and invoke that callback from within that method:
var consultaCep = function(callback){
request('https://viacep.com.br/ws/${cep}/json/', function (error, response, body) {
if (!error && response.statusCode == 200) {
status = "succeeded";
callback(null, {status : status});
} else {
callback(error);
}
})
}
app.get('/', function(req, res){
consultaCep(function(err, result){
if(err){
res.send(500, { error: 'Something went wrong' });
} else {
res.send(result);
}
});
});
As we know, we must return the response in the express app to avoid "Cannot set headers after they are sent to the client" error.
However, In below code, I'm trying to return the response but It's returning to our router and causes mentioned error. how I can directly return the response in function?
router.post("/admins", async function (req, res) {
var newAdminObj = await newAdminObjectDecorator(req.body, res);
var newAdmin = new Admins(newAdminObj)
newAdmin.save(function (err, saveresult) {
if (err) {
return res.status(500).send();
}
else {
return res.status(200).send();
}
});
});
// the function
var newAdminObjectDecorator = async function (entery, res) {
// doing some kinds of stuff in here
// if has errors return response with error code
if (err) {
// app continues after returning the error header response
return res.status(500).send();
}
else {
return result;
}
}
Never run a response operation other than the controller's functions. Let the other function return the answer and decide according to the answer.
router.post("/admins", async function (req, res) {
var newAdminObj = await newAdminObjectDecorator(req.body);
if (newAdminObj instanceof Error) {
return res.status(500).send()
}
var newAdmin = new Admins(newAdminObj)
newAdmin.save(function (err, saveresult) {
if (err) {
return res.status(500).send();
}
else {
return res.status(200).send();
}
});
});
// the function
var newAdminObjectDecorator = async function (entery) {
// doing some kinds of stuff in here
// if has errors return response with error code
if (err) {
// app continues after returning the error header response
return err;
}
else {
return result;
}
}
Its possible to force a route ?
Example:
I have this route A:
notiSchema = notification model
router.get('/set', function(req, res){
User.findById("userId". function(err, foundUser){
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
if(req.user.notifications.length === 0){
req.user.notifications.unshift(notiSchema);
req.user.save();
} else {
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
req.user.save();
}
});
}
});
});
res.json(req.user.notifications);
});
Problem here is that the 'res.json' line is read before the userB is updated
So i created this other route B:
router.get('/get', middleware.isLoggedIn, function(req, res){
res.json(req.user.notifications);
});
My Ajax:
$.get('/set', function(data){
// I only add a "fa-spin" class here
}).then(function(){
$.get('/get', function(data){
$(data).each(function(i, item){
$('.notDrop').prepend(item);
});
// Remove the "fa-spin" class
});
});
But sometimes route "B" is called before "A" ends;
So i want to know if its possible to call the "B" route only after the "A" one gets totally finished.
I rewrote your route to accumulate all the changes into req.user.notifications and then just save once at the end (if the array was modified). This allows you to then have only one .save() operation and to know when it's done by passing a callback to it.
Summary of changes:
Accumulate results in the array and only save at the end.
Only save if the array was modified.
Get rid of the special case for .length === 0 as that is not needed.
Use a callback on req.user.save() to know when it's done so we can then. send the response after the save is done.
Add error handling for .save().
Add error handling for .findById()
Here's the code:
router.get('/set', function(req, res){
User.findById("userId", function(err, foundUser){
if (err) {
console.log(err);
res.status(500).send("Error finding user.")
return;
}
let origLength = req.user.notifications.length;
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
}
});
});
if (req.user.notifications.length !== origLength) {
req.user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error saving user notifications.")
} else {
res.json(req.user.notifications);
}
});
} else {
res.json(req.user.notifications);
}
});
});
If you change your db code so you get an array of users from the find operation, then you can process those like this:
router.get('/set', function(req, res){
User.find({_id: {$in: arrayOfIds}}, function(err, foundUsers){
if (err) {
console.log(err);
res.status(500).send("Error finding user.")
return;
}
let origLength = req.user.notifications.length;
foundUsers.forEach(function(foundUser) {
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
}
});
});
});
if (req.user.notifications.length !== origLength) {
req.user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error saving user notifications.")
} else {
res.json(req.user.notifications);
}
});
} else {
res.json(req.user.notifications);
}
});
});
I am working on a middleware function that should check in a db if the logged in user has the role = 2 before allowing access to the requested page. If the user does not have the role = 2 he should be redirected to the home page (/). I wrote following function to achieve that:
isAdmin = function(req, res, callback) {
let Users = require('./models/users');
Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
The following function gets placed in the app.get(/admin) part of my routes file
function ensureAdmin(req, res, next) {
if (isAdmin()) {
return next();
}
console.log(colors.red('[ATTENTION] ') + colors.red('A non admin tried to access admin-only pages'));
res.redirect('/');
}
app.get:
app.get('/admin', ensureAuthenticated, ensureAdmin, function(req, res){
res.send('Admin panel!');
});
When I try to access the /admin page I just get a ReferenceError: isAdmin is not defined. Possibly there are more errors after this one that I can't solve so it would be great if anyone could tell me what I did wrong and fix the code if he wants. I am a node.js beginner :)
EDIT (new code):
module.exports = function(isAdmin){
var isAdmin = function(req, res, callback) {
if(req.user.steam.role === undefined || req.user.steam.role != 2){
return callback(null, false);
} else {
if(req.user.steam.role === 2){
return callback(null, true);
}
}
};
}
.
let isAdmin = require('../functions/isAdmin');
function ensureAdmin(req, res, next) {
if(isAdmin()) {
return next();
}
}
Do an export on your function isAdmin if you are in different files and do return that function as it's async
var isAdmin = function(req, res, callback) {
let Users = require('./models/users');
return Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
export default isAdmin
Also, the call needs to be thenable
function ensureAdmin(req, res, next) {
isAdmin().then(response => {
next();
});
}
I noticed that you have written console.log res.redirect which will not make sense after calling next() in middleware. You can shift this console.log() prior to the next() call. Avoid res.redirect() in middleware
Last, Assuming that you are doing an import of a file as well as mentioned by #hansmaad
Frist you have to export your isAdmin function from the file where it is implemented
export default isAdmin
and then require it in the file where you want to use it
const isAdmin = require('../middlewares/isAdmin'); // wherever you've put this file
As your isAdmin function is async and returns a promise, you have to call next() when this promise resolved.
isAdmin().then(() => next(), () => res.redirect('/'));