本文件是基於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; };