搭建網站時使用的express,是基於connect模塊開發的。這篇博客是通過分析connect源碼來分析中間件隊列處理HTTP請求的過程。源碼參考地址:https://github.com/senchalabs/connect/blob/master/index.js。文中如有不正確敬請指出。
在express中我們使用中間件是類似這樣的(圖片來自http://www.infoq.com/cn/articles/nodejs-connect-module):
1. 入口文件connect.js一共做了一件事:
export createServer函數。
下圖只提取部分(我覺得比較有代表性比較關鍵)功能的代碼。
2. 所以執行connect() 也就是執行createServer(),具體做了如下事情:
1)返回一個函數app,入口參數為req, res。app有很多屬性方法,比如app.use, app.handle, app.listen,也支持事件。app的函數體就是執行app.handle(req, res, next)。
2)為app添加了stack屬性,用來保存中間件。初始值為空數組。
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共享。