乐趣区

Express 文档(错误处理)

错误处理
错误处理是指 Express 如何捕获和处理同步和异步发生的错误,Express 附带一个默认的错误处理程序,因此你无需编写自己的错误处理程序即可开始使用。
捕捉错误
确保 Express 捕获运行路由处理程序和中间件时发生的所有错误非常重要。
路由处理程序和中间件内的同步代码中发生的错误不需要额外的工作,如果同步代码抛出错误,则 Express 将捕获并处理它,例如:
app.get(“/”, function (req, res) {
throw new Error(“BROKEN”); // Express will catch this on its own.
});
对于由路由处理程序和中间件调用的异步函数返回的错误,必须将它们传递给 next() 函数,Express 将捕获并处理它们,例如:
app.get(“/”, function (req, res, next) {
fs.readFile(“/file-does-not-exist”, function (err, data) {
if (err) {
next(err); // Pass errors to Express.
}
else {
res.send(data);
}
});
});
如果将任何内容传递给 next() 函数(字符串 ’route’ 除外),则 Express 将当前请求视为错误,并将跳过任何剩余的非错误处理路由和中间件函数。
如果序列中的回调不提供数据,只提供错误,则可以按如下方式简化此代码:
app.get(“/”, [
function (req, res, next) {
fs.writeFile(“/inaccessible-path”, “data”, next);
},
function (req, res) {
res.send(“OK”);
}
]);
在上面的示例中,next 作为 fs.writeFile 的回调提供,调用时有或没有错误,如果没有错误,则执行第二个处理程序,否则 Express 会捕获并处理错误。
你必须捕获由路由处理程序或中间件调用的异步代码中发生的错误,并将它们传递给 Express 进行处理,例如:
app.get(“/”, function (req, res, next) {

setTimeout(function () {
try {
throw new Error(“BROKEN”);
}
catch (err) {
next(err);
}
}, 100);
});
上面的示例使用 try…catch 块来捕获异步代码中的错误并将它们传递给 Express,如果省略 try…catch 块,Express 将不会捕获错误,因为它不是同步处理程序代码的一部分。
使用 promises 可以避免 try…catch 块的开销或者使用返回 promises 的函数,例如:
app.get(“/”, function (req, res, next) {
Promise.resolve().then(function () {
throw new Error(“BROKEN”);
}).catch(next); // Errors will be passed to Express.
});
由于 promises 会自动捕获同步错误和拒绝 promises,你可以简单地提供 next 作为最终的 catch 处理程序,Express 将捕获错误,因为 catch 处理程序被赋予错误作为第一个参数。
你还可以使用处理程序链来依赖同步错误捕获,通过将异步代码减少为一些简单的代码,例如:
app.get(“/”, [
function (req, res, next) {
fs.readFile(“/maybe-valid-file”, “utf8”, function (err, data) {
res.locals.data = data;
next(err);
});
},
function (req, res) {
res.locals.data = res.locals.data.split(“,”)[1];
res.send(res.locals.data);
}
]);
上面的例子有一些来自 readFile 调用的简单语句,如果 readFile 导致错误,那么它将错误传递给 Express,否则你将快速返回到链中下一个处理程序中的同步错误处理的世界。然后,上面的示例尝试处理数据,如果失败,则同步错误处理程序将捕获它,如果你在 readFile 回调中完成了此处理,则应用程序可能会退出,并且 Express 错误处理程序将无法运行。
无论使用哪种方法,如果要调用 Express 错误处理程序并使应用程序存活,你必须确保 Express 收到错误。
默认错误处理程序
Express 附带了一个内置的错误处理程序,可以处理应用程序中可能遇到的任何错误,此默认错误处理中间件函数添加在中间件函数堆栈的末尾。
如果你将错误传递给 next() 并且你没有在自定义错误处理程序中处理它,它将由内置错误处理程序处理,错误将堆栈跟踪写入客户端,堆栈跟踪不包含在生产环境中。
将环境变量 NODE_ENV 设置为 production,以在生产模式下运行应用程序。
如果在开始写入响应后调用 next() 并出现错误(例如,如果在将响应流式传输到客户端时遇到错误),则 Express 默认错误处理程序将关闭连接并使请求失败。
因此,当你添加自定义错误处理程序时,必须在 headers 已发送到客户端时委托给默认的 Express 错误处理程序:
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render(‘error’, { error: err})
}
请注意,如果你在你的代码调用 next() 出现错误多次,则会触发默认错误处理程序,即使自定义错误处理中间件已就绪也是如此。
编写错误处理程序
以与其他中间件函数相同的方式定义错误处理中间件函数,除了错误处理函数有四个参数而不是三个:(err, req, res, next),例如:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send(‘Something broke!’)
})
你可以在其他 app.use() 和路由调用之后定义错误处理中间件,例如:
var bodyParser = require(‘body-parser’)
var methodOverride = require(‘method-override’)

app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(function (err, req, res, next) {
// logic
})
中间件函数内的响应可以是任何格式,例如 HTML 错误页面、简单消息或 JSON 字符串。
对于组织(和更高级别的框架)目的,你可以定义多个错误处理中间件函数,就像使用常规中间件函数一样,例如,为使用 XHR 和不使用 XHR 的请求定义错误处理程序:
var bodyParser = require(‘body-parser’)
var methodOverride = require(‘method-override’)

app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)
在此示例中,通用 logErrors 可能会将请求和错误信息写入 stderr,例如:
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
同样在此示例中,clientErrorHandler 定义如下,在这种情况下,错误会明确传递给下一个错误。
请注意,在错误处理函数中不调用“next”时,你负责编写(和结束)响应,否则这些请求将“挂起”,并且不符合垃圾回收的条件。
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({error: ‘Something failed!’})
} else {
next(err)
}
}
实现“catch-all”的 errorHandler 函数,如下所示(例如):
function errorHandler (err, req, res, next) {
res.status(500)
res.render(‘error’, { error: err})
}
如果你有一个具有多个回调函数的路由处理程序,则可以使用 route 参数跳转到下一个路由处理程序,例如:
app.get(‘/a_route_behind_paywall’,
function checkIfPaidSubscriber (req, res, next) {
if (!req.user.hasPaid) {
// continue handling this request
next(‘route’)
}
else{
next();
}
}, function getPaidContent (req, res, next) {
PaidContent.find(function (err, doc) {
if (err) return next(err)
res.json(doc)
})
})
在此示例中,将跳过 getPaidContent 处理程序,但 app 中的 /a_route_behind_paywall 中的任何剩余处理程序将继续执行。
对 next() 和 next(err) 的调用表明当前处理程序已完成并处于什么状态,next(err) 将跳过链中的所有剩余处理程序,除了那些设置为处理上述错误的处理程序。

上一篇:使用模板引擎
下一篇:调试

退出移动版