搭建網站時使用的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共享。