假设我使用express的Router()获得了这段代码。
authRoute.param('token', function(req, res, next, token) {
User.findOne({ 'local.resetPasswordToken': req.params.token, resetPasswordExpires: { $gt: Date.now() }}, function(err, user) {
if (!user) {
req.flash('error', 'error msg here');
return res.redirect('/forgot');
}
req.user = user;
})
next();
})
authRoute.route('/reset/:token')
.get(function(req, res) {
res.render('reset', {
user: req.user
})
})
我得到这个错误:
_http_outgoing.js:346
throw new Error('Can't set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
如果没有Router(),它看起来就是这样:
app.get('/reset/:token', function(req, res) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error', 'Password reset token is invalid or has expired.');
return res.redirect('/forgot');
}
res.render('reset', {
user: req.user
});
});
});
Router.param端出了什么问题?我阅读了给出的错误消息,了解到当试图在已经设置的响应上设置标头时会发生这种情况。
您正在authRout.param中立即调用next()
,从而允许其他中间件在调用Mongoose回调之前,在某个时刻调用res.end()
(res.render、res.send等)。我的假设是,当允许中间件执行(前面解释过),然后在其中一个中间件已经"结束"响应后调用res.redirect('/forgot')
时,会出现此错误。
解决方案是在继续中间件链之前等待数据库响应,这只是一个异步问题。考虑以下内容:
authRoute.param('token', function(req, res, next, token) {
User.findOne({ 'local.resetPasswordToken': req.params.token, resetPasswordExpires: { $gt: Date.now() }}, function(err, user) {
// pass error to middlewares
if (err) return next(err)
// Stop the middleware chain i.e. return res.redirect
if (!user) {
req.flash('error', 'error msg here');
return res.redirect('/forgot');
}
req.user = user;
// important! call `next()` after mongo response
next()
})
})
在单个请求处理程序(后一个示例)中不会发生这种情况,因为您正确地处理了async,而不是在调用Mongoose回调之前结束http响应。