I'm trying to run a middleware inside another middleware. When I call the nested middleware, I get an error saying:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Here's my code:
// middleware1
module.exports = function(req, res, next) {
req.user = 'current user';
next();
};
// middleware2
module.exports = function(req, res, next) {
middleware1(req, res, next);
if (req.user !== 'current user') return res.status(403).send('Access denied');
next();
};
// API
router.get('/api', middleware2, async (req, res) => {
return res.send(req.user);
});
What am I doing wrong, and what's the correct way to chain or nest a middleware into another one?
(The web app has more to it, but I only included whatever is necessary to reproduce the error)
Following on #jknotek comments:
Since you are passing the next function from middleware2 to middleware1, you are risking that the final middleware gets called in the part of the call stack, which triggers res.send. Afterwards it proceeds in middleware2, which tries to do a res.status, which will fail.
Either you want to chain middlewares as:
router.get('/api', middleware1, middleware2, [...])
Or you would want your middleware2 to behave somewhat like:
//middleware2
module.exports = function(req, res, next) {
middleware1(req, res, () =>
if (req.user !== 'current user') return res.status(403).send('Access denied');
next();
);
};
Related
Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)
I have two middleware functions attached to my app get request which works fine.
const express = require('express');
const app = express();
function fun1 (req, res, next) {
console.log('this is fun1')
next()
}
function fun2 (req, res, next) {
console.log('this is fun2')
next()
}
app.get('/', fun1, fun2, function (req, res, next) {
res.send('User Info')
})
app.listen(8080, () => console.log(`Listening on port 8080!`))
Now if I try to do next('test') in fun1 then it bypass fun2 and does 'test' output in browser window instead of 'User Info' which is correct. But how do I get data in fun2? I need to pass something from fun1 and get it in fun2 for further validation.
Assign it to req. You will have access to the same request and response objects through all middlewares.
Note that next('test') does not respond to the client or at least it is not meant to. It is meant to handle errors. Without an error handler and in development mode, Express shows these errors in the browser.
Read on:
Error handling in Express
You can do this by attaching a key-value pair with req` object.
Now how to do this,
const express = require('express');
const app = express();
function fun1 (req, res, next) {
req.MY_VAR = 'MY_VAL'; // setting the value
console.log('this is fun1')
next()
}
function fun2 (req, res, next) {
let myVar = req.MY_VAR; // retrieving the value
console.log(myVar); // MY_VAL
console.log('this is fun2')
next()
}
app.get('/', fun1, fun2, function (req, res, next) {
res.send('User Info')
})
app.listen(8080, () => console.log(`Listening on port 8080!`))
Now, why not next()? Generally, the value passed in next() will be received by the error argument in app.get('/', function (err, req, res, next) {} );
Came accros a sequence of code execution which I found unusual here is the code:
server.js
const Actions_Single_PVC = require('./routes/Actions_single_PVC.js');
app.use('/Actions_single_PVC', Actions_Single_PVC);
app.use((err, req, res, next) => {
console.log('invalid token');
});
Actions_single_PVC.js
router.post('/', asyncMW(async (req, res, next) => {
throw new Error();
}));
router.use((err, req, res, next) => {
console.log('error');
}
And in case you have never seen this construction before here is asyncMW:
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
module.exports = asyncMiddleware;
What I didn't understand was that when an error is thrown (I reproduced it here with throw new Error();) that the error handling middleware in the server.js file is executed. I expected that the error handling middleware of the Actions_single_PVC.js would get executed.
Question:
Why is the error middlware in server.js executed and not the error middlware in Actions_single_PVC.js?
It is because the following code applies middleware to only request with base path matching Actions_single_PVC.
app.use('/Actions_single_PVC', Actions_Single_PVC);
Whereas following code is apply middleware to all global requests.
app.use((err, req, res, next) => {
console.log('invalid token');
});
If you'll hit the url /Actions_single_PVC then the middlewares in Actions_single_PVC will get hit.
I am trying to implement a middleware that will check if a user is authenticated before the server delivers a page. Although it looks like the process of doing this is simple, node is throwing an error which says "Can't set headers after they are sent".
My router's code is:
module.exports = function(app) {
app.get('/', checkAuth, require('./myAuthenticatedPage').get);
app.get('/login', require('./myLoginPage').get);
};
The myAuthenticatedPage.js:
exports.get = function(req, res) {
res.render('index');
};
The myLoginPage.js:
exports.get = function(req, res) {
res.render('login');
};
The checkAuth.js:
module.exports = function (req, res, next) {
if(!req.session.user) {
res.redirect('/login');
}
next();
}
Any help on this will be greatly appreciated.
Thanks
If you aren't authenticated, you'll redirect the user and then try to render the index page. This causes the http headers to be sent twice, hence the error "Can't set headers after they are sent".
In checkAuth.js try:
module.exports = function (req, res, next) {
if(!req.session.user) {
res.redirect('/login');
} else {
next();
}
}
I was wondering how should I be dealing with response and next in express.
My understanding is that when I say res.send(...) -> returns a response
If I want to throw an error, I say next(new Error('whatever')) -> auto sets the http status code using express errorhandler.
I am able to do either of those but not but, looks like I am messing up.
Can anyone give an example?
I tried,
somefunc(err, req, res, next) {
if(err) {
res.send(500, 'some error');
return next(err);
}
}
return [somefunc, express.errorHandler()];
thanks.
You can register some middleware to handle errors:
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
You can also simply send a 500 with your response if you encounter an error in your logic
function loginUser(req, res, next) {
try {
var valid;
//code to check user
if (valid) {
next();
} else {
res.send(401, 'not authorized');
}
} catch (err) {
res.send(500, 'Oopsy');
}
}
app.get('/some/route',loginUser, function(req, res) {
// ...
});
Just skip return next(); part. Use return res.send(500,'some error'); Calling next causes next middleware to be called which IMO is not what you want in this case. I wrote more about it here.
Here is minimal example of express stack:
express = require('express');
app = express();
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(app.router());
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res) {
if (some error)
res.send(500, 'Some error');
else
res.send('<body>Hello</body>');
});
app.listen(3000); // listen on port 3000
The get request will be called by router middleware. Middlewares are parsed as chain. You can use custom middleware in this chain, for example:
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(function(req, res, next){
if (some condition)
res.send(500, 'No way'); // this will break the request chain
else
next(); // this will continue processing and will call app.router middleware
});
app.use(app.router());
app.use(express.static(__dirname + '/public'));
});
app.router middleware is responsible for calling appropriate method based on requested URL (like app.get('/')). If it fails to find one, it calls next() to pass control to express.static middleware which tries to find static file in /public/ folder.