express的router实现往简单来讲就是两类数组,第一类数组全局之后一个,用于保存所有添加到app中的路由以及中间件(实际上在express中中间件也是一种路由),第二类数组用于保存每个路由上添加的中间件。
app.get('/page1', function(req, res) {});
app.post('/page2', function(req, res, next){}, function(req, res) {});
通过以上方式添加的路由调用以下代码:
// application.js
methods.forEach(function(method){
app[method] = function(path){
// 1. 根据路径创建一个路由并将该路由保存到router的数组中,见 1.1
var route = this._router.route(path);
// 2. 在该路由中再次创建一个数组,用于保存该路由对应的处理方法以及中间件,见 2.1
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
// router/index.js
// 1.1
proto.route = function route(path) {
var route = new Route(path);
// 路由数组中的Layer主要就行path的匹配,参数解析等等
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
// 这个dispatch是遍历对应的路由上的方法执行,见1.1.1
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
};
// router/route.js
// 2.1
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
// 真正的添加到路由上面的方法和中间件被保存到这个数组中的Layer中
var handle = handles[i];
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});
// router/route.js
// 1.1.1
Route.prototype.dispatch = function dispatch(req, res, done) {
var idx = 0;
var stack = this.stack;
next();
function next(err) {
// 见 1.1.1.1
layer.handle_request(req, res, next);
}
};
// router/layer.js
// 1.1.1.1
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
// 调用真正的handle
fn(req, res, next);
};
以上就是路由添加的基本流程,保留了主要的代码。下面看一下路由匹配的过程。主要代码就是遍历router中的Layer。
// router/index.js
// express接收到请求之后就是调用这个方法进行路由匹配的
proto.handle = function handle(req, res, out) {
var self = this;
var idx = 0;
// middleware and routes
var stack = self.stack;
next();
function next(err) {
// find next matching layer
var layer;
var match;
var route;
// 直到匹配到一个或者stack被遍历完为止
while (match !== true && idx < stack.length) {
layer = stack[idx++];
match = matchLayer(layer, path);
route = layer.route;
// 如果当前Layer和path不匹配,继续匹配
if (match !== true) {
continue;
}
// 通过app.use或者router.use添加的中间件route=undefined,
// 但是此时match=true,详见Layer的match实现。
// 所以app.use或者router.use添加的中间件都可以被匹配到。
if (!route) {
continue;
}
}
// no match
if (match !== true) {
return done(layerError);
}
// 如果最终匹配到一个Layer就执行它,并且把next方法传给我们,
// 我们在处理完我们中间
// 件的业务之后需要调用next方法while循环才会继续执行以匹配其他的中间件和路由。
layer.handle_request(req, res, next);
}
以上就是express的路由添加和匹配的核心流程和代码,当然删去了很多复杂的细节只留下了主要流程,此外express还可以通过app.use、app.param、route.all等等方式添加路由,基本的逻辑都是一样的,不再赘述。