Express/Connect对于错误处理有个约定,见 http://expressjs.com/guide.html 的Error handling部分,这里约定express的错误处理统一采用如下方式
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
错误处理这段描述大意是这样的:”错误处理中间件必须按规则定义,它必须有4个参数。” 关于错误处理有个示例,在Express项目的example中如下地址:
https://github.com/visionmedia/express/blob/master/examples/web-service/index.js#L52
他如此强调**“四个参数"**让我我很好奇,中间件的处理流程,错误处理机制是怎样实现,我偏不四个参数会怎样?便忍不住想一探究竟,也就开始了分析。
我们知道express是依赖connect而建立的,整个中间件的处理跟错误处理机制都都是在connect中,因此我就直接扒开了connect的源码,其实说句实话代码不多,但是刚开始阅读的时候还是挺不容易看明白的。看了网上别人的一些文章,分析的也很粗略,如下:
http://www.infoq.com/cn/articles/nodejs-connect-module
只讲了大致原理,可是大致原理大家都懂,我好奇具体流程是怎么实现的,经过反复阅读后,终于把思路整理如下,大家可以对照:
https://github.com/senchalabs/connect/blob/master/lib/proto.js#L101
来看的话效果会更好。(本来做想做流程图的,但是发现流程图画出来更加让人难以理解,我选择用中文做“伪代码”,对于猿猿们应该跟好理解,省略了其他的不重要的代码,比如一些路径的处理代码,这不在我好奇的范围内。)
阅读之前一定要熟悉一下两点:
1、“层”,“中间件”是可以理解为相同概念,可以看到所有文档中都是描述为“中间件”,connect当初的设计思路是希望像洋葱那样一层层的运行的,因此在代码用的是**“layer”**。
2、文中**“[直接]”**表示return了,当前代码不再往下执行了,而是转而执行return后面的内容了。
/**
* Handle server requests, punting them down
* the middleware stack.
*
* [@api](/user/api) private
*/
app.handle = function(req, res, out) {
var stack = this.stack
function next(err) {
[......]
layer = stack[index++];
if (/*已经遍历完所有层*/) {
if (/*有未处理的错误*/) {
//对于错误的处理(格式化错误信息,打印输出)
} else {
//404错误的处理(格式化错误信息,打印输出)
}
return;
}
try {
if (/*当前层的路径不匹配*/) {
//把当前请求[直接]交给下一个层(中间件)处理
};
if (/*存在尚未处理的错误*/) {
if (/*当前层的handle的参数个数是否为4*/) {
//如果当前层的handle的参数个数为4的话意味着当前层即错误处理层
//而如果当层的handle的参数个数不为4的话,则当前层不是错误处理层,则[直接]交给下一层处理
//如果下一层仍旧不是错误处理层的话将不断往下传,直到交给错误处理层,如果没有任何错误处理层的话,到最后会调用默认的错误处理
layer.handle(err, req, res, next);
} else {
//交给下一层处理
next(err);
}
} else if (/*如果参数个数小于4*/) {
//当前层的handle的参数个数小于4意味这当前层是正常业务逻辑处理层
//当没有尚未处理的错误,并且当前当前层是正常业务逻辑处理层,则调用本层的handle处理
layer.handle(req, res, next);
} else {
//如果没有尚未处理的错误信息,但是当前层又不是正常业务逻辑处理层,则[直接]交给下一层处理
next();
}
} catch (e) {
next(e);
}
}
//启动next()
next();
};