错误处理是指Express如何捕获和处理同步和异步发生的错误,Express附带一个默认的错误处理程序,所以你无需编写本身的错误处理程序便可开始使用。json
确保Express捕获运行路由处理程序和中间件时发生的全部错误很是重要。segmentfault
路由处理程序和中间件内的同步代码中发生的错误不须要额外的工做,若是同步代码抛出错误,则Express将捕获并处理它,例如:promise
app.get("/", function (req, res) { throw new Error("BROKEN"); // Express will catch this on its own. });
对于由路由处理程序和中间件调用的异步函数返回的错误,必须将它们传递给next()
函数,Express将捕获并处理它们,例如:app
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会捕获并处理错误。ide
你必须捕获由路由处理程序或中间件调用的异步代码中发生的错误,并将它们传递给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将不会捕获错误,由于它不是同步处理程序代码的一部分。ui
使用promises
能够避免try...catch
块的开销或者使用返回promises的函数,例如:this
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)
将跳过链中的全部剩余处理程序,除了那些设置为处理上述错误的处理程序。