We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
还没用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路由的实现。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
还没用
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
路由的实现。The text was updated successfully, but these errors were encountered: