毛病处置惩罚
毛病处置惩罚是指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)
将跳过链中的一切盈余处置惩罚顺序,除了那些设置为处置惩罚上述毛病的处置惩罚顺序。