如果您在发送后尝试写信给res,就会发生这种情况,因此您必须在res.render() 之后调用其他代码,或者您在调用之前已经响应。
将其更改为return res.render(...),使其退出函数,否则它将继续通过函数并命中其他res.render()s等。
那个错误处理程序也有问题。我将在几分钟内更新我的帖子(通过电话)。它可能应该有 (req, res, next) 并调用 return next(err) 并将其传递给您的错误处理中间件。
这是我喜欢在 async/await Express 中使用的模式:
// these routes occur in the order I show them
app.get('/route', async (req, res, next) => {
try {
const data = 'asdf'
const payload = await something(data)
.then((result) => createPayload(result))
// remember, if you throw anywhere in try block, it will send to catch block
// const something = willFail().catch((error) => {
// throw 'Custom error message:' + error.message
// })
// return from the route so nothing else is fired
return res.render('route', { payload })
} catch (e) {
// fire down to error middleware
return next(e)
}
})
// SPLAT
app.get('*', async (req, res, next) => {
// if no matching routes, return 404
return res.status(404).render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if err !== null, this middleware fires
// it has a 4th input param "err"
res.status(500).render('error/500')
// and do whatever else after...
throw err
})
注意:在没有参数的情况下调用next() 回调被视为没有错误,并继续执行下一个中间件。如果传入anything,它将触发错误中间件,参数为错误处理中间件中err的值。只要错误中间件最后出现,您就可以在路由和其他中间件中使用此技术。请注意将return 与res.send/render() 一起使用,以防止重复设置标题。
新:
.then() 中有一个回调,看起来有点不对劲。我从逻辑上看不出err 的来源,因为已解决的promise 的值作为result 进入.then() 函数。此时,它是可疑的,应尽可能删除或重构。这部分在这里:
try {
let results = [];
await Search.search(options).then(result => {
results = result;
}, err => {
throw err;
});
console.log("res.render");
return res.render("partial/search.pug", {user: user, search: {
query: string,
results: results
}});
} catch(err) {
next(err);
}
首先,这里是我希望看到的异步/等待语法:
router.get("/", async (req, res, next) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if (s.indexOf(",") > -1) {
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": { tags: { $all: searchedTags }, _forSale: true }
};
// If a promise is ever rejected inside a try block,
// it passes the error to the catch block.
// If you handle it properly there, you avoid unhandled promise rejections.
// Since, we have async in the route function, we can use await
// we assign the value of Search.search(options) to results.
// It will not proceed to the render statement
// until the entire promise chain is resolved.
// hence, then(data => { return data }) energizes `results`
const results = await Search.search(options)
.then(data => data)
// If any promise in this chain is rejected, this will fire
// and it will throw the error to the catch block
// and your catch block should pass it through to your
// error handling middleware
.catch(err => { throw 'Problem occurred in index route:' + err });
return res.render("partial/search.pug", {
user: user, search: {
query: string,
results: results
}
});
} catch (err) {
// look at the top how we added next as the 3rd, callback parameter
return next(err);
}
});
module.exports = router;
错误处理程序:
// notice how we add `err` as first parameter
app.use((err, req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
来自 Express 文档:
以与其他中间件函数相同的方式定义错误处理中间件函数,除了错误处理函数有四个参数而不是三个:(err, req, res, next)。例如:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
http://expressjs.com/en/guide/error-handling.html
这可能是您真正的问题,因为错误处理程序应该仅在使用 any 输入调用 next() 时触发,但您的似乎每次都像普通的中间件,所以我怀疑是因为该中间件函数上没有err参数,所以它被视为普通的。
默认错误处理程序
Express 带有一个内置的错误处理程序,可以处理应用程序中可能遇到的任何错误。这个默认的错误处理中间件函数被添加到中间件函数栈的末尾。
如果你将错误传递给 next() 并且你没有在错误处理程序中处理它,它将由内置的错误处理程序处理;错误将通过堆栈跟踪写入客户端。堆栈跟踪不包含在生产环境中。
如果您在开始编写响应后调用 next() 时出现错误(例如,如果您在将响应流式传输到客户端时遇到错误),Express 默认错误处理程序将关闭连接并使请求失败。
因此,当您添加自定义错误处理程序时,当标头已发送到客户端时,您将希望委托给 Express 中的默认错误处理机制:
// code example in docs
请注意,如果您多次调用 next() 并在代码中出现错误,则可能会触发默认错误处理程序,即使自定义错误处理中间件已到位。
我还建议使用错误处理程序中间件上方的 splat 路由 app.get('*', async (req, res, next) => {})(又名作为列表中最后加载的路由)。这将捕获所有不匹配的路由,例如 /sih8df7h6so8d7f 并将客户端转发到您的 404。我认为错误处理程序中间件更适合错误 500 和干净的格式化类型错误,因为它为您提供了一个可以解析 @987654346 值的函数@ 任何时候从路由调用它。
我通常使用 JSON Web 令牌(作为每个身份验证所需路由中的第一行代码)进行身份验证失败:
if (!req.person) return res.status(403).render('error/403')
我知道其中一些可能fry your wig wholesale,所以在确定是否要使用它之前尝试所有这些东西并查看每个部分的工作情况。