在Node.js中无法使用try/catch处理异步回调函数中的异常。对于异步异常,Node提供了两种处理方式:callback回调、'error'事件。而在Express框架中,基于回调传递错误及错误处理中间件,可以捕获系统中所有异步异常并进行统一处理。
1. next()方法与错误传递
Express是一个Web框架,它对Node的HTTP核心模块中对象进行了扩展,提供了更方便的HTTP请求与响应处理方式。在Express中进行错误处理时,首先要使用next()方法将错误向下传递。
next()是app.METHOD()或router.METHOD()方法回调函数中的第三个参数。通过next()方法,可以将请求定向到下一个处理函数或另一个路由中,next()方法也可以用于传递错误。
在HTTP请求中,我们可能会使用next()方法传递一个状态或错误:
传递一个HTTP状态
对于不存在的某一个或某一类路由,可以使用next()方法传递一个404状态:
router.all('/user/*', function(req, res, next){
next(404);
});
当出现异常时,可以使用next()方法传递一个500状态:
router.get('/a/path', function(req, res, next){
fs.readFile('a/file', function(err, data){
if(err){
// 打印一个标准错误,并使用next()方法传递一个500状态
console.error(err);
next(500);
} else {
res.send('一些响应信息');
}
});
});
传递错误信息
请求处理过程中出错时,也可以使用next()方法直接传递这个错误:
router.get('/a/path', function(req, res, next){
fs.readFile('a/file', function(err, data){
if(err){
// 使用next()方法传递错误信息
next(err);
} else {
res.send('一些响应信息');
}
});
});
使用Promise进行异步处理时,也可以通过next()方法传递出现的异常:
router.get('/a/path', function(req, res, next){
promise.then(function(result) {
// 操作成功
res.send('一些响应信息');
}).catch(function(err) {
// 操作失败,使用next()方法传递错误信息
next(err);
});
});
2. 使用中间处理错误
Express中的中间件分为两类:通过router.use()方法挂载的路由中间件和通过app.use()方法挂载的应用中间件,前者只对某一个或一类路由起做用,而后者会在应用范围内起作用。
处理异常时,一般会在全局处理。在app.js文件中,Express提供了以下处理异常的中间件:
// 捕获404并定向到错误处理
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// 错误处理
// 开发环境下的错误处理
// 会输出堆栈信息
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
// 设置响应状态
res.status(err.status || 500);
// 渲染错误处理页
res.render('error', {
message: err.message,
error: err,
layout: false
});
});
}
// 生产环境下的错误处理
// 不会向用户显示堆栈信息
app.use(function(err, req, res, next) {
// 设置响应状态
res.status(err.status || 500);
// 渲染错误处理页
res.render('error', {
message: err.message,
error: {},
layout: false
});
});
对于定义的路由,都会由上面第一个中间件捕获,在这里会生成一个404的错误,并将错误向下传递到错误处理路由。
Express提供了两个错误中间件(处理程序),分别用于开发/生产两种模式下的错误处理。在开发模式下,我们一般会输出错误的堆栈信息,以方便调试。而在生产模式下,一般会显示一个错误提示页(如:404-页面未找到、500-服务器内部错误等)。只需要在这两个中间件中渲染不同的页面,就可以实现在不同环境下,对不同错误的统一响应和处理。
