nodejs express 框架解密4-路由
本文档是基于express3.4.6
express 的路由是自己去实现的,没有使用connect中的路由中间件模块。
1、在如何创建一个app那篇中,我们提到了路由,
//router //路由 this._router = new Router(this); this.routes = this._router.map; this.__defineGetter__('router', function(){ this._usedRouter = true; this._router.caseSensitive = this.enabled('case sensitive routing'); this._router.strict = this.enabled('strict routing'); return this._router.middleware; });
可以看到,是将Router这个类存储到了app上的_rounter这个属性上去了,然后将所有得路由映射存储到了routes 这个属性上了。最后在给app定义一个router属性,直接调用了这个模块的
middleware模块。我们来看看Router类
function Router(options) { options = options || {}; var self = this; this.map = {}; this.params = {}; this._params = []; this.caseSensitive = options.caseSensitive; this.strict = options.strict; this.middleware = function router(req, res, next){ self._dispatch(req, res, next); }; }
从上面的代码可以看出,middleware调用了自身的_dispatch 函数 ,这个函数的作用就是路由分发
Router.prototype._dispatch = function(req, res, next){ var params = this.params , self = this; debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl); // route dispatch (function pass(i, err){ var paramCallbacks , paramIndex = 0 , paramVal , route , keys , key; // match next route function nextRoute(err) { pass(req._route_index + 1, err); } // match route req.route = route = self.matchRequest(req, i); // implied OPTIONS if (!route && 'OPTIONS' == req.method) return self._options(req, res); // no route if (!route) return next(err); debug('matched %s %s', route.method, route.path); // we have a route // start at param 0 req.params = route.params; keys = route.keys; i = 0; // param callbacks function param(err) { paramIndex = 0; key = keys[i++]; paramVal = key && req.params[key.name]; paramCallbacks = key && params[key.name]; try { if ('route' == err) { nextRoute(); } else if (err) { i = 0; callbacks(err); } else if (paramCallbacks && undefined !== paramVal) { paramCallback(); } else if (key) { param(); } else { i = 0; callbacks(); } } catch (err) { param(err); } }; param(err); // single param callbacks function paramCallback(err) { var fn = paramCallbacks[paramIndex++]; if (err || !fn) return param(err); fn(req, res, paramCallback, paramVal, key.name); } // invoke route callbacks function callbacks(err) { var fn = route.callbacks[i++]; try { if ('route' == err) { nextRoute(); } else if (err && fn) { if (fn.length < 4) return callbacks(err); fn(err, req, res, callbacks); } else if (fn) { if (fn.length < 4) return fn(req, res, callbacks); callbacks(); } else { nextRoute(err); } } catch (err) { callbacks(err); } } })(0); };
这个函数是通过pass 这个自动执行的函数进行路由转发的,
首先通过请求, req.route = route = self.matchRequest(req, i); 来配置路由,返回需要的信息
可以看到matchRequest 函数返回了(我访问了下http://localhost:3000)
{ path: '/', method: 'get', callbacks: [ [Function] ], keys: [], regexp: /^\/\/?$/i, params: [] }
看看 matchRequest 这个函数
Router.prototype.matchRequest = function(req, i, head){ var method = req.method.toLowerCase() , url = parse(req) , path = url.pathname , routes = this.map , i = i || 0 , route; // HEAD support if (!head && 'head' == method) { route = this.matchRequest(req, i, true); if (route) return route; method = 'get'; } // routes for this method if (routes = routes[method]) { // matching routes for (var len = routes.length; i < len; ++i) { route = routes[i]; if (route.match(path)) { req._route_index = i; return route; } } } };
它返回一个路由的处理结果。
后面根据参数执行了param() 函数。这个函数主要是处理callback回调函数的。
2.给路由注册各种函数:
methods.forEach(function(method){ app[method] = function(path){ if ('get' == method && 1 == arguments.length) return this.set(path); // deprecated if (Array.isArray(path)) { console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0'); } // if no router attached yet, attach the router if (!this._usedRouter) this.use(this.router); // setup route console.log(method,'test2'); this._router[method].apply(this._router, arguments); return this; }; });
这个函数直接添加了开始的注册函数,下面的这个methods.foreach 依次为每一个app.get,app.post 等等 添加路由和 callback 函数
Router.prototype.route = function(method, path, callbacks){ var method = method.toLowerCase() , callbacks = utils.flatten([].slice.call(arguments, 2)); // ensure path was given if (!path) throw new Error('Router#' + method + '() requires a path'); // ensure all callbacks are functions callbacks.forEach(function(fn){ if ('function' == typeof fn) return; var type = {}.toString.call(fn); var msg = '.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); }); // create the route debug('defined %s %s', method, path); var route = new Route(method, path, callbacks, { sensitive: this.caseSensitive, strict: this.strict }); // add it (this.map[method] = this.map[method] || []).push(route); return this; }; Router.prototype.all = function(path) { var self = this; var args = [].slice.call(arguments); methods.forEach(function(method){ self.route.apply(self, [method].concat(args)); }); return this; }; methods.forEach(function(method){ Router.prototype[method] = function(path){ var args = [method].concat([].slice.call(arguments)); this.route.apply(this, args); return this; }; });
最后一个函数,直接执行,给路由添加get,post等函数。
在Router.prototype.route 函数中,我们调用了一个:
var route = new Route(method, path, callbacks, { sensitive: this.caseSensitive, strict: this.strict });
Route 是一个路由的基本单元,包含2个方法:
function Route(method, path, callbacks, options) { options = options || {}; this.path = path; this.method = method; this.callbacks = callbacks; this.regexp = utils.pathRegexp(path , this.keys = [] , options.sensitive , options.strict); } /** * Check if this route matches `path`, if so * populate `.params`. * * @param {String} path * @return {Boolean} * @api private */ Route.prototype.match = function(path){ var keys = this.keys , params = this.params = [] , m = this.regexp.exec(path); if (!m) return false; for (var i = 1, len = m.length; i < len; ++i) { var key = keys[i - 1]; var val = 'string' == typeof m[i] ? utils.decode(m[i]) : m[i]; if (key) { params[key.name] = val; } else { params.push(val); } } return true; };