首页 > 代码库 > express源码剖析(3)
express源码剖析(3)
express/appliction.js
application.js对外的方法可以分成四类:
- 设置/初始化成员变量,主要是对成员变量settings进行设置,这样的方法有:enabled、enabled、disabled、 disable和set方法。
- 设置路由和对每个路由设置中间件:这样的方法有:use.router,all,param和methods方法。
- 设置模板引擎的方法:engine和render。
- 启动node服务器对res和req操作的方法:listen和handler。
1.解析app.engine方法
express的api中有app.engine方法,例如:
app.engine("handlebars",handlebars.engine);
看下这个函数的的核心代码就是:
// get file extension,ext为文件扩展名 var extension = ext[0] !== ‘.‘ ? ‘.‘ + ext : ext; // store engine this.engines[extension] = fn;
1.2 解析app.use方法
var express = require(‘express‘); var app = express(); function handlerWrap(){ console.log("1"); } app.use("/", handlerWrap);
代码直接进入app.user中的下列代码:
//lazyrouter函数不对外提供 this.lazyrouter();
它可以说是整个express的核心代码:
app.lazyrouter = function lazyrouter() { //给app这个对象定义私有变量_router,它是一个对象Router。 if (!this._router) { this._router = new Router({ caseSensitive: this.enabled(‘case sensitive routing‘), //false strict: this.enabled(‘strict routing‘) //false }); this._router.use(query(this.get(‘query parser fn‘))); //this.get(‘query parser fn‘)为一个函数:
//middleware.init(this)也是一个处理函数
this._router.use(middleware.init(this)); } };
尤其是给app.router赋值之后,初始化下列代码:
this._router.use(query(this.get(‘query parser fn‘))); //this.get(‘query parser fn‘)为一个函数: this._router.use(middleware.init(this));
//必须要值得注意的事情是:router.use(path,fn); 实际上是为path建立layer,有多少个fn,就建立多少个Layer,然后把这个layer压入router.stack数组中。
看看router的构造函数,它的定义和app的定义很类似。
if (!fn || !fn.handle || !fn.set) { //handlerWrap函数为handler、 set成员! return router.use(path, fn); }
// 进入router/index.js中,执行proto.use()方法。
// 实际上是给router.stack添加一个layer成员,
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn);
layer.route = undefined; //给layer.route赋值undefined
this.stack.push(layer);
1.3 解析router.route
ayer.js作为中间件封装的数据结构,接着看Layer包:
function Layer(path, options, fn) { if (!(this instanceof Layer)) { // this是Layer的实例 return new Layer(path, options, fn); } debug(‘new %s‘, path); var opts = options || {}; this.handle = fn; this.name = fn.name || ‘<anonymous>‘; this.params = undefined; this.path = undefined; this.regexp = pathRegexp(path, this.keys = [], opts); if (path === ‘/‘ && opts.end === false) {
this.regexp.fast_slash = true; } }
接下来看看pathRegexp,http://blog.csdn.net/chszs/article/details/51055229。
rounter.use直接把该函数构造成一个Layer成员,实际上是给router.stack添加一个layer成员。
同理:this._router.use(middleware.init(this))的作用是把该函数:
return function expressInit(req, res, next){ if (app.enabled(‘x-powered-by‘)) res.setHeader(‘X-Powered-By‘, ‘Express‘); req.res = res; res.req = req; req.next = next; req.__proto__ = app.request; res.__proto__ = app.response; res.locals = res.locals || Object.create(null); next(); };
构造成一个Layer成员,实际上是给router.stack添加一个layer成员。
这样在express初始的时候,rounter的stack数组中有两个回调函数了。如果说调用app.user(fn),都是给rounter的stack数组中添加回调函数。
执行过程:
执行的入口函数是:
var app = function(req, res, next) { app.handle(req, res, next); };
它调用application.js中handler函数,一般来说next函数为没有的,在handler函数中封装了一个callback:
// final handler, 默认就是next var done = callback || finalhandler(req, res, { env: this.get(‘env‘), one rror: logerror.bind(this) });
finalhandler是一个npm包,可以在github上看它的作用,它的作用就是一个http请求的最后一步的处理方式。
接着请求会转向rounter.handler函数,在handler函数中,最终是执行next(),在看next()之前,先来弄清除几个回调函数,第一个:done:
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‘), one rror: logerror.bind(this) }); */ var done = restore(out, req, ‘baseUrl‘, ‘next‘, ‘params‘);
/*
done = function(err){
给req赋值,重新储存‘baseUrl‘, ‘next‘, ‘params‘,
返回fn.apply(this,arguments);
}
*/
next()函数中如果没有获取到req中的pathname,那么它会执行:
if (path == null) { //layerError为undefined return done(layerError); } /* 结果是:out.apply(undefined,undefined);最终转向finalhandler执行完毕 */
最后,重点弄清楚的代码是:
while (match !== true && idx < stack.length) { layer = stack[idx++]; //第一个Layer match = matchLayer(layer, path); // route = layer.route; if (typeof match !== ‘boolean‘) { // hold on to layerError layerError = layerError || match; } if (match !== true) { continue; } if (!route) { // process non-route handlers normally continue; } if (layerError) { // routes do not match with a pending error match = false; continue; } var method = req.method; var has_method = route._handles_method(method); // build up automatic options response if (!has_method && method === ‘OPTIONS‘) { appendMethods(options, route._options()); } // don‘t even bother matching route if (!has_method && method !== ‘HEAD‘) { match = false; continue; } } // no match if (match !== true) { return done(layerError); } // store route for dispatch on change if (route) { req.route = route; } // Capture one-time layer values req.params = self.mergeParams ? mergeParams(layer.params, parentParams) : layer.params; var layerPath = layer.path; // this should be done for the layer self.process_params(layer, paramcalled, req, res, function (err) { if (err) { return next(layerError || err); } if (route) { return layer.handle_request(req, res, next); } trim_prefix(layer, layerError, layerPath, path); }); }
看看Layer.matchLayer(layer, path),他返回是true/false;不过会给该Layer添加:params = {};path 和keys。
重点代码:
fns.forEach(function (fn) { // non-express app,fn && fn.handler && fn.set为true if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); } debug(‘.use app under %s‘, path); //给fn添加两个变量 fn.mountpath = path; fn.parent = this; // restore .app property on req and res router.use(path, function mounted_app(req, res, next) { var orig = req.app; fn.handle(req, res, function (err) { req.__proto__ = orig.request; res.__proto__ = orig.response; next(err); }); }); // mounted an app fn.emit(‘mount‘, this); }, this);
1.2 send函数解析:
1 function send(req, res, status, body) { 2 function write() { 3 res.statusCode = status 4 5 // security header for content sniffing 6 res.setHeader(‘X-Content-Type-Options‘, ‘nosniff‘) 7 8 // standard headers 9 res.setHeader(‘Content-Type‘, ‘text/html; charset=utf-8‘) 10 res.setHeader(‘Content-Length‘, Buffer.byteLength(body, ‘utf8‘)) 11 //请求方法为head,不需要发送body,header的作用多用于自动搜索机器人获取网页的标志信息,或者rss种子信息,或者传递安全认证信息等。 12 if (req.method === ‘HEAD‘) { 13 res.end() 14 return 15 } 16 17 res.end(body, ‘utf8‘) 18 } 19 20 if (isFinished(req)) { //req流已经结束 21 write() //给res写入信息,并且返回给客户端 22 return 23 } 24 25 // unpipe everything from the request 26 unpipe(req) //取消在pipe中设置的通道 27 28 // flush the request 29 onFinished(req, write) //req流完成之后,触发write事件。 30 req.resume() //开启req流 31 }
注意,express在finalhandler包中重写了流的unpipe的方法,在onfinished包中。
express源码剖析(3)