歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> node.js模塊connect源碼分析

node.js模塊connect源碼分析

日期:2017/2/28 15:57:25   编辑:Linux教程
connect是一個web server中間件。 使用方法: var connect = require('connect');
connect(
connect.static(__dirname + '/public', { maxAge: 0 })
, function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.end('<img src="/tobi.jpeg" />')
}
).listen(3000);

思路:

通過connect創建一個http|https server,提供http server的所有功能。

connect是原型繼承於http server的,它會用use到的中間件替換掉server的requestListener。

通過connect.use(route, handle)來對每一個路由添加中間件,這些中間件handle會與route綁定保存在一個stack裡面,每次有request請求的時候,遍歷這個堆,找到對應route的handle,執行handle,如果handle最後調用了next(),就會繼續尋找並執行下一個匹配的handle。

通過封裝handle,可以很容易的在connect基礎上添加更多的middleware。

connect.js

有一個createServer方法,可以通過connect()訪問到。根據第一個參數,如果是object,就當作是https的選項,創建HTTPSServer,如果第一個參數不是object,則創建HTTPServer,所有的參數(除了https的選項)都是一個中間件handle,會在HTTPServer綁定到‘/’路徑上。

HTTPSServer是在HTTPServer的基礎上添加了一層,可以啟用HTTPS服務。

同時,connect.js會讀取middleware文件夾,把裡面的中間件讀取到,為他們創建getter,可以通過connect.static()訪問到。

而每一個中間件文件暴露在外的函數都是返回一個handle。

http.js

HTTPServer:初始化的時候會把所有的參數當作handle存放進stack,然後以handle方法為requestListener調用http.Server方法。

HTTPServer隨後會繼承http.Server的原型。

use(route, handle)

把handle去除外殼之後綁定到route上面,存入stack中。

handle(req, res, next)

遍歷整個stack,尋找到req.url與route匹配的元素,執行它的handle。當所有的元素都遍歷完還有錯誤,則輸出。

util.js

這是一個工具包,裡面包含了用到的各種工具函數。

pause(obj)

把傳遞進來的obj對象的'data'和'end‘事件都保存下來,返回兩個函數:end():不再保存事件。resume():停止保存並把之前保存的事件釋放出去給obj再次捕獲,達到暫停這個obj對象的效果。(感覺可能會有bug,如果在這裡釋放的時候又有'data'或者'end'事件觸發會不會導致順序變亂?)

parseCookie(str)

把str以;或者,為分隔符分開。每一個都是一個cookie鍵值對,然後再以=分開。去除value的引號。每個鍵只能被取得一次。

中間件:

router

connect的route使用方法和express類似。 1 connect(connect.route(function(app){
2 app.get('/:id', middle1, middle2, cb);
3 app.post('/admin', cbpost);
4 }));
route.js內有一個_methods數組,存放所有的route請求方法名稱。(get/post/put/...)。 methods對象和routes對象根據_methods內的名稱,包含著響應的元素,如:methods['get'], routes['get']。
methods對象,根據_methods數組內的方法名稱,為每一個方法調用來一個生產函數,這個函數首先把routes對象內的成員賦值[],然後返回一個函數,這個函數用來產生routes的內容。 methods還有一個元素param,調用它可以為path中出現了某個param的時候設置對應的處理方法。 例如: app.param('id', function(req, res, next, val){}), 當path中有param id出現的時候,會先調用這個注冊的函數再進行後面的操作。
在進行完上述對象的初始化之後,route模塊會進行fn.call(this, methods)的調用,即用methods作為參數調用傳遞進來的匿名函數。所以在app.get('/:id', cb, cb1);的時候,實際調用的是methods.get('/:id', cb, cb1),而methods.get即是之前生產函數的返回函數。 這個函數的處理:cb為這條路由的handle,middle1..middle2..等中間件函數將會存放在cb.middleware數組中(這裡會產生一個bug)。然後把'/'轉化成為正則對象,然後在轉化正則的時候,可能會遇到路徑裡面有:id等key,會把這些key存放到keys裡面。 最終的routes內將會多處一條routes['GET']的記錄:

GET:
[ { fn: [Object],
path: /^\/(?:([^\/]+?))\/?$/i,
keys: [Object],
orig: '/:id',
method: 'GET' } ]

剛才說會產生一個bug,是當有兩條以上的route以cb作為handle的時候: app.get('/:id', middle1, middle2, cb);
app.get('/:id/test', middle3, cb); 因為最終的handle都是cb,此時cb的middleware數組會在第二次處理get的時候把第一次的覆蓋掉,造成第一次的middleware被替換。
至此,所有的准備工作完成了,然後會返回一個router函數作為handle。
實際request請求觸發的時候:
作為handle的router函數被調用,先通過match(req, routes, i)函數,查找req.method對應方法的route的path,與req.pathName匹配。找到路徑匹配的把這個route內的這個對象內的fn,同時把keys params method存放到fn裡面整合稱為一個route返回。返回的route內容形式為 { [Function]
middleware: [ [Function], [Function] ],
keys: [ 'id' ],
method: 'GET',
params: [ id: 'ca' ] } 然後函數去尋找是否通過methods.param定義了這條route中的param的處理函數,如果有,在這裡就執行完對應param的處理函數。之後執行middleware數組內的函數,最後執行這個route。即上一段中說到的fn。這之中能夠鏈式執行下去的條件是中間函數都執行了next(),繼續調用下去,當然也可以其中某個函數就結束整個處理。

bodyParser

bodyParser用來解析post方法傳遞過來的參數。 只接受mime類型為 application/x-www-form-urlencoded application/json multipart/form-data 三種的非GET和HEAD請求。
application/x-www-form-urlencoded通過模塊qs.parse來解析。 application/json通過JSON.parse解析。 multipart/form-data是文件上傳,通過formidable解析。


static
static是一個靜態文件服務器。 connect.static(root, options)會產生一個handle,handle設置默認的options然後調用send函數。
options內容: root:靜態服務器的根路徑,必須由connect.static傳入。 path:訪問的文件路徑 getOnly:訪問方法限制(默認是true:只允許get方法訪問 ) maxAge:時間限制 redirect:在訪問的路徑是目錄的時候,如果允許redirect,則會redirect到這個目錄下的index.html文件,默認為true callback:在每次靜態服務之後調用的函數(包括發生錯誤,發生錯誤之後不會再調用next)。 hidden:是否允許訪問隱藏文件(默認為false)
根據這些參數來決定訪問限制。
支持conditional和range。
最終通過 var stream = fs.createReadStream(path, opts); stream.pipe(res); 管道的方式來傳送文件內容。
Copyright © Linux教程網 All Rights Reserved