Open CommanderXL opened 5 years ago
还没用express写过server,先把部分源码撸了一遍,各位大神求轻拍。
express
server
express入口文件在lib文件夹下的express.js,其向外界暴露了一些方法。
lib
express.js
最主要的(express.js 第36-47行):
function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); //各中间件的处理入口,handle方法通过mixin拓展于proto }; mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; } exports = module.exports = createApplication;
我们经常在自己的业务代码中这样写:
var express = require('express'); var app = express();
其实就是调用createApplication方法.这样就实例化了一个app。这个app比较特殊,通过mixin集成了一些其他的属性
createApplication
app
mixin
mixin(app, EventEmitter.prototype, false); //拓展了事件发射器原型对象 mixin(app, proto, false); //拓展了application.js中的属性和方法
在我们业务代码实例化app的时候,调用了app.init()方法完成了一些初始化的配置。init()方法也是从application.js中继承的。
app.init()
init()
application.js
入口文件很清晰,主要是完成方法的暴露以及app的一些初始化操作。
接下来看下application.js中的部分代码逻辑:
第136-146行,延迟实例化一个_router
_router
app.lazyrouter = function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); this._router.use(query(this.get('query parser fn'))); this._router.use(middleware.init(this)); } };
第157-174行,这是各个middleware的入口,
middleware
app.handle = function handle(req, res, callback) { var router = this._router; //获取已经实例化得router // final handler 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); //当http过来时,对于request和response的处理从这个地方开始 };
第187-242行,app.use方法提供了应用级的middleware,但是事实上在214行,this.lazyrouter()新建一个route,第219-221行,然后根据app.use(fn)传入的参数挂载到了route.use()路由级中间件上了。app.use()是route.use的一个代理。
app.use
this.lazyrouter()
app.use(fn)
route.use()
app.use()
route.use
app.use = function use(fn) { var offset = 0; var path = '/'; // default path to '/' // disambiguate app.use([fn]) if (typeof fn !== 'function') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== 'function') { offset = 1; path = fn; } } var fns = flatten(slice.call(arguments, offset)); //铺平arguments if (fns.length === 0) { throw new TypeError('app.use() requires middleware functions'); } // setup router this.lazyrouter(); //如果没有route实例则新建一个 var router = this._router; fns.forEach(function (fn) { // non-express app //如果传入的不是express实例app if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); //将中间件注入到router中 } debug('.use app under %s', path); 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); return this; };
第255-258行,代理到router实例的route()的方法中:
router
route()
app.route = function route(path) { this.lazyrouter(); return this._router.route(path); };
router实例是通过router/index.js提供构造函数来创建的,在这个文件夹中第42-60行:
router/index.js
var proto = module.exports = function(options) { var opts = options || {}; function router(req, res, next) { router.handle(req, res, next); //handle方法继承于proto } // mixin Router class functions router.__proto__ = proto; router.params = {}; router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; router.stack = []; //初始化一个stack.这个stack中保存了注册的所有中间件 return router; };
提供了一个router的构造函数,它的原型对象上,第136行提供了proto.handle方法,这个方法的作用就是接收来自http的req和res。
proto.handle
http
req
res
第413行,提供了proto.use方法,正如上面所说的app.use是route.use的代理,这个方法的特殊性就在任何的http请求都会经过在app.use上挂载的中间件,例如现在express4.x已经将很多中间件从自身移除,需要你重新通过npm去安装,然后在业务代码中进行引用,例如使用body-parser中间件:
proto.use
express4.x
npm
body-parser
var express = require('express'); var app = express(); var bodyParser = require('body-parser'); app.use(bodyParser.json());
这样每次http请求过来的时候首先会经过bodyParser.json()这个中间件,它提供了一个向req添加req.body = {}方法,并传向下一个中间件的作用。 同时在route.use内部,第439-458行,
bodyParser.json()
req.body = {}
for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== 'function') { throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn)); } // add the middleware debug('use %s %s', path, fn.name || '<anonymous>'); var layer = new Layer(path, { //新建一个layer,layer上挂载了error_handler和request_handler sensitive: this.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; this.stack.push(layer); //route自身会维护一个stack,将每个新建的layer都推入stack当中,这个layer实例最终会对匹配的path,作出error_handle或者request_handle。 }
第477行,proto.route方法提供了一个新建route的方法。
proto.route
proto.route = function route(path) { var route = new Route(path); //新建一个route,这个Route构建函数内部实现见./route.js,它里面提供了一个空的stack,用以 var layer = new Layer(path, { //新建一个layer,layer的作用见下面的讲解 sensitive: this.caseSensitive, strict: this.strict, end: true }, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer); //新建一个route,这个route会维护自身的stack return route; };
var route = require('express').Router(),但是这个方法不同的地方 在于,它会自身维护一个stack,这个stack中有你在这个方法上面定义的所有中间件。同样,你可以通过这个route挂载对于不同路径的req, res的处理。
var route = require('express').Router()
stack
route
使用的方法:
var express = require('express'); var app = express(); var router = express.Router(); //没有挂载任何路径的中间件,通过该路由的每个请求都会执行该中间件 router.use(function(req, res, next) { console.log('route.use'); }) router.get('/test', function(req, res, next) { console.log('route.get'); }); //最后需要将这个router挂载到应用 app.use('/', router);
以上部分主要是整个express的中间件的挂载。总结一下:
router.use()
layer
'/'
var router = require('express').Router()
app.use('/', router);
接下来讲下当http请求到来的时候,数据的流向: 在你定义中间件的过程中,因为是维护了一个app或者route实例,它们分别都有一个stack。这个stack是FIFO的,因此每当一个请求过来的时候,数据从最开始的定义的中间件开始,一直向下按顺序进行传递,因此你可以自己定义,当然,你需要调用next()方法。就比如Route.protoype.dispath方法
FIFO
Route.protoype.dispath
//将req, res分发给这个route Route.prototype.dispatch = function dispatch(req, res, done) { var idx = 0; var stack = this.stack; if (stack.length === 0) { return done(); } var method = req.method.toLowerCase(); if (method === 'head' && !this.methods['head']) { method = 'get'; } req.route = this; next(); function next(err) { if (err && err === 'route') { return done(); } var layer = stack[idx++]; if (!layer) { return done(err); } if (layer.method && layer.method !== method) { //匹配传入的req请求方式,和layer的method进行对比 return next(err); } //调用layer.handle,用以错误处理或者request处理 if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); } } };
最后,http请求的处理: 在app或者route实例中,自身有一个stack,这个stack就存放了在挂载中间时新建的layer,每个layer实例都保存了对应的路径,以及相应的error_handle和request_handle。
error_handle
request_handle
谢谢大家看到这里,欢迎大家斧正。
下一篇写写express路由的实现。
还没用
express
写过server
,先把部分源码撸了一遍,各位大神求轻拍。express
入口文件在lib
文件夹下的express.js
,其向外界暴露了一些方法。最主要的(
express.js
第36-47行):我们经常在自己的业务代码中这样写:
其实就是调用
createApplication
方法.这样就实例化了一个app。这个app
比较特殊,通过mixin
集成了一些其他的属性在我们业务代码实例化
app
的时候,调用了app.init()
方法完成了一些初始化的配置。init()
方法也是从application.js
中继承的。入口文件很清晰,主要是完成方法的暴露以及
app
的一些初始化操作。接下来看下
application.js
中的部分代码逻辑:第136-146行,延迟实例化一个
_router
第157-174行,这是各个
middleware
的入口,第187-242行,
app.use
方法提供了应用级的middleware
,但是事实上在214行,this.lazyrouter()
新建一个route,第219-221行,然后根据app.use(fn)
传入的参数挂载到了route.use()
路由级中间件上了。app.use()
是route.use
的一个代理。第255-258行,代理到
router
实例的route()
的方法中:router
实例是通过router/index.js
提供构造函数来创建的,在这个文件夹中第42-60行:提供了一个
router
的构造函数,它的原型对象上,第136行提供了proto.handle
方法,这个方法的作用就是接收来自http
的req
和res
。第413行,提供了
proto.use
方法,正如上面所说的app.use
是route.use
的代理,这个方法的特殊性就在任何的http
请求都会经过在app.use
上挂载的中间件,例如现在express4.x
已经将很多中间件从自身移除,需要你重新通过npm
去安装,然后在业务代码中进行引用,例如使用body-parser
中间件:这样每次
http
请求过来的时候首先会经过bodyParser.json()
这个中间件,它提供了一个向req
添加req.body = {}
方法,并传向下一个中间件的作用。 同时在route.use
内部,第439-458行,第477行,
proto.route
方法提供了一个新建route的方法。var route = require('express').Router()
,但是这个方法不同的地方 在于,它会自身维护一个stack
,这个stack
中有你在这个方法上面定义的所有中间件。同样,你可以通过这个route
挂载对于不同路径的req
,res
的处理。使用的方法:
以上部分主要是整个
express
的中间件的挂载。总结一下:app.use()
挂载的中间件最终都代理到了router.use()
方法下router.use()
方法,新建一个layer
,layer
上保存了路径,默认为'/'
,及相应的处理方法,并存入这个app
维护的stack
中。var router = require('express').Router()
新建的router
路径级实例,同样可以挂载不同的中间件,不过最后需要将这个router
路由注入到app
应用当中:app.use('/', router);
接下来讲下当
http
请求到来的时候,数据的流向: 在你定义中间件的过程中,因为是维护了一个app
或者route
实例,它们分别都有一个stack
。这个stack
是FIFO
的,因此每当一个请求过来的时候,数据从最开始的定义的中间件开始,一直向下按顺序进行传递,因此你可以自己定义,当然,你需要调用next()方法。就比如Route.protoype.dispath
方法最后,
http
请求的处理: 在app
或者route
实例中,自身有一个stack
,这个stack
就存放了在挂载中间时新建的layer
,每个layer
实例都保存了对应的路径,以及相应的error_handle
和request_handle
。谢谢大家看到这里,欢迎大家斧正。
下一篇写写
express
路由的实现。