先看最簡單的node的hello world
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337);
上面這段就是來一個請求,就用傳給createServer的匿名函數來處理請求。
使用Express的代碼
var app = express(); //...中間忽略 http.createServer(app).listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); });
對比可以看出,執行express()后,會返回一個函數,賦值給app,app的簽名應該為:
function(req,res){//...}
然后請求都會被app這個函數處理(因為這個app是執行express后的結果,下面將不加區分的使用app和express兩個詞)。
可以認為,在express內部,有一個函數的數組,暫時叫這個數組tasks,每來一個請求express內部會依次執行這個數組中的函數(這里說依次並不嚴謹,每個函數必須滿足一定條件才行,這個后面說),應該可以想到,在這個函數數組里,每個函數的簽名應該像下面那樣:
function(req,res){//...}
但是,實際上是:
function(req,res,next){//...}
這個next,是指下一個函數。后面我們會寫一些試驗來體驗一下這個next,先總結一下:
對於一個典型的使用express的app.js,做了以下幾件事
- 1.導入相關模塊
- 2.執行過 var app = express() 后,
- 使用app.set 設置express內部的一些參數(options)
- 使用app.use 來注冊函數,可以簡單的認為是向那個(被我叫做)tasks的數組進行push操作
- 3.通過http.createServer 用app來處理請求
試驗一: 向express中注冊自定義函數
注冊進express中的函數,需要滿足(請見下面更正)
1.長成下面這個樣子:
function(req,res,next){ //...我們自己的邏輯 next(); }
2.app.use(customerFunc) 要寫在下面兩句的前面:
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
關於第2點,是因為路由后或請求靜態資源后,一次請求響應的生命周期實質上已經結束,加在這后面進行請求處理,沒有任何意義。
關於第1點,寫點代碼就好了:
app.use(function(req,res,next){ console.log("111"); next(); });
如果不寫next(),那么后面注冊的函數就不會執行,運行試一下就知道了。
再來一個:
app.use(function(req,res,next){ console.log('111'); next(); console.log('222'); }); app.use(function(req,res,next){ console.log("333"); next(); });
那么控制台的輸出的順序是:111 333 222
更正:
上面說,自定義的函數應該滿足兩個條件,一般使用是那樣。但是,也可以兩個都不滿足。。。比如,自定義函數可以是4參數的,放在最后做通用error處理。:
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
在上面兩句之后可以加一個:
app.use(function(err,req,res,next){ if(err){ //自己的處理錯誤的邏輯 console.log(err.message); console.log(err.stack); res.end('404') } });
試驗二: next()的工作原理
在理解的上面的過程后,能不能不借助express,自己實現上面的過程呢,主要是怎么處理next()那一塊:
function express(){ var funcs = []; var expr = function(req,res){ var i = 0; function next(){ var task = funcs[i++]; if(!task) return; task(req,res,next); } next(); } expr.use=function(f){ funcs.push(f); } return expr; } var app = express(); app.use(function(req,res,next){ console.log('haha'); next(); }); app.use(function(req,res,next){ console.log('hehe'); next(); }); app.use(function(req,res){ res.end("there is nothing happened"); }); http.createServer(app).listen('3000', function(){ console.log('Express server listening on port 3000'); });
啟動服務后,每來一個請求,控制台會依次輸出haha hehe,然后瀏覽器是there is nothing happened
當然如果要更深一步,可以去看原代碼,實際上這一部分的主要代碼是在connect中的,在connect/lib/proto.js 這個源文件中,主要是app.use,和app.handle 兩個函數中。