搭建网站时使用的express,是基于connect模块开发的。这篇博客是经过分析connect源码来分析中间件队列处理HTTP请求的过程。源码参考地址:https://github.com/senchalabs/connect/blob/master/index.js。文中若有不正确敬请指出。node
在express中咱们使用中间件是相似这样的(图片来自http://www.infoq.com/cn/articles/nodejs-connect-module):git
1. 入口文件connect.js一共作了一件事:github
export createServer函数。express
下图只提取部分(我以为比较有表明性比较关键)功能的代码。json
2. 因此执行connect() 也就是执行createServer(),具体作了以下事情:数组
1)返回一个函数app,入口参数为req, res。app有不少属性方法,好比app.use, app.handle, app.listen,也支持事件。app的函数体就是执行app.handle(req, res, next)。闭包
2)为app添加了stack属性,用来保存中间件。初始值为空数组。app
3. 当执行app.use(require('body-parser').json())的时候执行了什么函数
1) 首先require('body-parser').json()返回一个函数parser,入口参数为req, res, next。函数执行到最后会调用next();网站
2) app.use是将{route: '/', handle: parser}加入stack.
一样的道理,app.use的过程是将中间件加入app.stack的过程。server启动前stack中已经有一系列中间件。
4. app.listen(3000)
建立server实例:http.createServer(app)。因此app是收到HTTP请求后的回调。
5. 当一个HTTP请求到来后,因为回调函数是app, 因此执行app.handle(req, res, next)。此时next是undefined。handle作了如下几件事
1)req.originalUrl = req.originalUrl || req.url。保存初始url
2)var done = next || finalHandler(req, res, {env: env, onerror: logerror})
3)定义next函数并执行。next函数作了如下几件事:
a. 获取下一个中间件。index初始为0
b. 若是layer为undefined, 在下一个事件循环执行done.
c. 取出layer中route, 若是跟当前url不匹配,执行next(), 也就是执行下一个中间件。若是匹配,执行call(layer.handle, route, err, req, res, next)
6. call函数作的事情是:
1)执行handle。若是没有错误,直接返回。handle函数中可能还会调用next, 好比bodyParser.json。若是有错误抛出,设置error。
2)若是出现了错误,执行next(error);
7. 中间件队列遍历结束,执行finalHandler。若是某个中间件直接执行res.send之类的操做,再也不执行next, 请求处理结束。
因此请求从发送到返回的过程就是req被若干个中间件函数处理,每一个函数对req有一点修改,而后传递给下一个中间件函数。next函数是一个闭包,而且一直做为参数被传递,index共享。