请求过程:express/appliction.js
server服务器启动:
//两个参数,端口和机器IP app.listen(port, host); app.listen = function listen() { /** this就是app函数、也是对象。 var app = function(req, res, next) { app.handle(req, res, next); }; */ var server = http.createServer(this); /* server为http.server的实例。 */ return server.listen.apply(server, arguments);
};
当服务器启动之后,它调用application.js中handler函数,执行过程是进入index.js中proto.handle函数,一个请求过来执行的入口是:
var app = function(req, res, next) { //application.js中的handle函数 app.handle(req, res, next); }; app.handle = function handle(req, res, callback) { var router = this._router; //一般来说next函数为没有的,在handler函数中封装了一个默认done,也就是callback,如下: var done = callback || finalhandler(req, res, { env: this.get('env'), onerror: logerror.bind(this) }); // no routes if (!router) { debug('no routes defined on app'); done(); return; } router.handle(req, res, done); };
在进入proto.handle函数之前,先看看两个包装器函数restore和wrap的功能:
1.restore和wrap
// manage inter-router variables baseUrl、url都是子app绑定到use(path,subApp)利用到的。 var parentParams = req.params; //初始时为undefined var parentUrl = req.baseUrl || ''; //初始时为"" //给baseurl和params重新赋值。 var done = restore(out, req, 'baseUrl', 'next', 'params'); function restore(fn, obj) { var props = new Array(arguments.length - 2); var vals = new Array(arguments.length - 2); for (var i = 0; i < props.length; i++) { props[i] = arguments[i + 2]; // 储存除fn,obj以外的参数 vals[i] = obj[props[i]]; // obj中的对象。'baseUrl', 'next', 'params' } return function(err){ // restore vals for (var i = 0; i < props.length; i++) { obj[props[i]] = vals[i]; } return fn.apply(this, arguments); }; } /*out是finalhandler(req, res, { env: this.get('env'), onerror: logerror.bind(this) }); */
wrap函数
// for options requests, respond with a default if nothing else responds if (req.method === 'OPTIONS') { done = wrap(done, function(old, err) { if (err || options.length === 0) return old(err); sendOptionsResponse(res, options, old); }); }//wrap是封装了旧的done函数,也就是next函数,执行的是fn.apply(this,args); function wrap(old, fn) { return function proxy() { var args = new Array(arguments.length + 1); args[0] = old; for (var i = 0, len = arguments.length; i < len; i++) { args[i + 1] = arguments[i]; } fn.apply(this, args); }; } // send an OPTIONS response function sendOptionsResponse(res, options, next) { try { var body = options.join(','); res.set('Allow', body); res.send(body); } catch (err) { next(err); } }
2.proto.handle
var self = this; debug('dispatching %s %s', req.method, req.url); //req.url不含域名:类似/m/login.html,第一个字符为“/” var search = 1 + req.url.indexOf('?'); //URL含?則path長度為search - 1 var pathlength = search ? search - 1 : req.url.length; //fqdn为true,只有可能是完整的url,如:http://www.baidu.com/dir/xy.html,其他路徑 //如:/dir/put.json或者:jj/tt.json都只能是 false var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://'); //protohost就是主机的名字,去掉协议 var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''; var idx = 0; var removed = ''; var slashAdded = false; var paramcalled = {}; // store options for OPTIONS request // only used if OPTIONS request var options = []; // middleware and routes var stack = self.stack; // manage inter-router variables // params和baseUrl都是undefined var parentParams = req.params; var parentUrl = req.baseUrl || ''; var done = restore(out, req, 'baseUrl', 'next', 'params'); // setup next layer req.next = next; // for options requests, respond with a default if nothing else responds // options請求方式,重新定義done if (req.method === 'OPTIONS') { done = wrap(done, function(old, err) { if (err || options.length === 0) return old(err); sendOptionsResponse(res, options, old); }); } // setup basic req values req.baseUrl = parentUrl; req.originalUrl = req.originalUrl || req.url;
下面的next会递归遍历router和中间件
next(); function next(err) { //函数是个递归,功能:1.还原req.url,2.终止递归,3.遍历stack,匹配layer //初始化时err为undefined,err = "route"的情况在于try、catch抛出的异常。 var layerError = err === 'route' ? null : err; // remove added slash, if (slashAdded) { //如果url="/m/xx.html",那么url为"m/xx.html"; req.url = req.url.substr(1); slashAdded = false; } // restore altered req.url,还原req.url, if (removed.length !== 0) { req.baseUrl = parentUrl; req.url = protohost + removed + req.url.substr(protohost.length); removed = ''; } // no more matching layers,递归终止, if (idx >= stack.length) { //router中stack的Layer已经遍历完毕,立即执行回调函数done. setImmediate(done, layerError); return; } // get pathname of request var path = getPathname(req); //没有获取到req中的pathname,那么它会执行递归终止, if (path == null) { // 结果是:out.apply(undefined,undefined);最终转向finalhandler执行完毕, return done(layerError); } // find next matching layer var layer; var match; var route; //要匹配成功才能跳出循环 while (match !== true && idx < stack.length) { layer = stack[idx++];
//会抛出'router'错误 match = matchLayer(layer, path);
//通过app.use来定义的layer.route=undefined,通过router.route(path)来定义的layer.route不为undefine. route = layer.route; if (typeof match !== 'boolean') { // hold on to layerError layerError = layerError || match; //出错,layerError可能是'router' }
//没有匹配到,或者匹配出错、出错可能是'router' if (match !== true) { continue; }
//处理针对router.router来挂载的处理函数。 if (!route) { // process non-route handlers normally continue; } //如果route不是undefined,那么继续,同时在处理this.params对应key的函数出错,那么即使匹配到了下一个Layer,也会跳出next, if (layerError) { // routes do not match with a pending error match = false; continue; } var method = req.method; //匹配到Layer所对应的router支持的方法 var has_method = route._handles_method(method); // build up automatic options response if (!has_method && method === 'OPTIONS') { //支持options请求方法。 appendMethods(options, route._options()); } // don't even bother matching route,请求时head,不需要返回。 if (!has_method && method !== 'HEAD') { match = false; continue; } } // 没有匹配到Layer,递归终止, if (match !== true) { return done(layerError); } // store route for dispatch on change if (route) { req.route = route; } // Capture one-time layer values,合并匹配到layer中path的参数对象,可以查看api。 req.params = self.mergeParams ? mergeParams(layer.params, parentParams) : layer.params;
//含正则,不是实际请求的path var layerPath = layer.path; // this should be done for the layer,如果说请求的url中带有参数,处理每个参数绑定的事情。 self.process_params(layer, paramcalled, req, res, function(err) {
//在paramCallback出现err,但是这个err绝对不是"router"。 if (err) { return next(layerError || err); //一个匹配router.stack中的一个Layer结束、进入stack的下一个元素进行匹配。 }
//通过route.route来定义的Layer。带有Layer.router = router. if (route) { return layer.handle_request(req, res, next); } trim_prefix(parentParams, layerError, layerPath, path); }); }
继续看process_params的处理函数:
proto.process_params = function process_params(layer, called, req, res, done) { var params = this.params; // captured parameters from the layer, keys and values var keys = layer.keys; // fast track, //沒有參數 if (!keys || keys.length === 0) { return done(); } var i = 0; var name; var paramIndex = 0; var key; var paramVal; var paramCallbacks; var paramCalled; //param和next函數的處理方式很類似。看代碼也忽略err的情況, // process params in order // param callbacks can be async, function param(err) { if (err) { return done(err); } if (i >= keys.length) { return done(); } paramIndex = 0; //m=["/user/anthonyliu/your","anthonyliu","your"] key = keys[i++]; //m = /user/anthonyliu/your if (!key) { return done(); } name = key.name; //区别params和req.params。params為this.params,也就是router.params。对应的是{key1:[f1,f2],key2:[f3,f4]....}。 //paramsCallbacks是通過router.params綁定的數組。 paramCallbacks = params[name]; //初始化時called[name]為undefined paramCalled = called[name]; if (paramVal === undefined || !paramCallbacks) { // return param(); } // param previously called with same value or error occurred // 正常情況下,調用一次就不需要調用了,或者發送錯誤,錯誤不等於‘route’ if (paramCalled && (paramCalled.match === paramVal || (paramCalled.error && paramCalled.error !== 'route'))) { // restore value // 为什么要重新存储value?因为在req.params对象中相同的key可能会有不同的value // 例如:第一层和第二层Layer的key相同而value不一样 req.params[name] = paramCalled.value; // next param,出錯會跳出递归!否則相同key对应相同的value所绑定的处理函数會跳過。 return param(paramCalled.error); } called[name] = paramCalled = { error: null, match: paramVal, value: paramVal }; paramCallback(); } // single param callbacks function paramCallback(err) { var fn = paramCallbacks[paramIndex++]; // store updated value, paramCalled.value = req.params[key.name]; if (err) { // store error paramCalled.error = err; param(err); return; } if (!fn) return param(); try {
//可能会重写 req.params[key.name],从而会导致需要 fn(req, res, paramCallback, paramVal, key.name); } catch (e) {
//err有两种值,一是"router",它不影响params以后的函数调用;另一个是“router”,它会终止params以后的函数调用。 paramCallback(e); } } param(); };
trim_prefix
//没有看懂时,这段代码要人命,看懂了都不好意思写注释了 function trim_prefix(layer, layerError, layerPath, path) { //path是请求的url,layerPath为Layer的路径匹配的字符串。 var c = path[layerPath.length]; if (c && '/' !== c && '.' !== c) return next(layerError); // Trim off the part of the url that matches the route // middleware (.use stuff) needs to have the path stripped //形成req.baseUrl = req.parentUrl + req.url或者req.url = req.originalUrl - req.baseUrl if (layerPath.length !== 0) { debug('trim prefix (%s) from url %s', layerPath, req.url); removed = layerPath; req.url = protohost + req.url.substr(protohost.length + removed.length); // Ensure leading slash if (!fqdn && req.url[0] !== '/') { req.url = '/' + req.url; slashAdded = true; } // Setup base URL (no trailing slash) req.baseUrl = parentUrl + (removed[removed.length - 1] === '/' ? removed.substring(0, removed.length - 1) : removed); } debug('%s %s : %s', layer.name, layerPath, req.originalUrl); if (layerError) { layer.handle_error(layerError, req, res, next); } else { layer.handle_request(req, res, next); } }
基本上到这里,express源码就结束了。