前言
express是當(dāng)下最熱門的基于node.js 的web框架嫁乘,它提供了一系列強大的特性:
- 路由
- 中間件
- 靜態(tài)文件服務(wù)
- 模板
- 錯誤處理
- params參數(shù)處理
- query解析 ...等
本文主要介紹路由及中間件杯瞻!
路由
首先我們要知道,express路由的使用方法
- 在app實例上調(diào)用
app.get('/', function (req, res, next) {
console.log(1);
next();
}, function (req, res, next) {
console.log(11);
next();
})
app.get('/2', function (req, res, next) {
console.log(2);
next();
}, function (req, res, next) {
console.log(3)
next()
})
-
上述代碼可見端圈,我們發(fā)現(xiàn)有幾個特點:
* 路由的處理函數(shù)可以有多個焦读;
* 路由的注冊也可以有多個;
* 調(diào)用next進(jìn)入下一個處理函數(shù)或路由舱权;
注冊路由
下邊我們就來看下express是如何注冊路由的:
源碼目錄:
目錄.png
首先在express.js源碼中我們可以看到這樣一句矗晃,說明app類繼承proto(即application.js)
var proto = require('./application');
mixin(app, proto, false);
接著進(jìn)入application中看到這樣一個方法,并且在源碼中多次調(diào)用宴倍;
// 看名字我們就知道這個是用來加載router的喧兄;并且將新建的router實例綁定到了this._router屬性上;
app.lazyrouter = function lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
接著下邊看到這樣的一段代碼啊楚;
// methods其實是一個npm的包啦吠冤,包含了通用的method類型:get、post等
// 下邊代碼可以看出循環(huán)在app中添加了多個屬性get恭理、post拯辙。。颜价。
// 并且關(guān)鍵的是調(diào)用 app.get(); 函數(shù)體中實際是調(diào)用了this._router.get()
methods.forEach(function(method){
app[method] = function(path){
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
this.lazyrouter();
var route = this._router.route(path);
route[method].apply(route, slice.call(arguments, 1));
return this;
};
})
// 代碼中可以看到
下邊就是路由的重頭戲了涯保,進(jìn)入到我們的Router文件中;
methods.concat('all').forEach(function(method){
proto[method] = function(path){
var route = this.route(path)
// 最終調(diào)用的get方法
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
* 看到這段代碼是不是很熟悉周伦,在application文件中有類似的代夕春;那么這個就不難理解,在proto屬性上添加了get专挪、post等屬性
* proto又是什么呢及志,往上翻一下就可以看到啦片排,其實就是導(dǎo)出對象,并且將所有的屬性繼承到了router方法上
* 這樣我們就不難聯(lián)想到速侈,在application中調(diào)用的get方法其實是調(diào)用這里的get率寡,那么我們來看它又做了什么; 它調(diào)用了this.route方法
proto.route = function route(path) {
var route = new Route(path);
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
};
* 在route方法中新建了一個Route的實例(route)和一個Layer的實例(layer),
* 將route掛載到layer上,
* 將layer添加到Router的stack中
* 最后導(dǎo)出route
上述代碼分析可以知道倚搬,在router上添加了一個layer(層)冶共,在layer存了一個route;并且Router中的get并不是最后的get方法
最后我們進(jìn)入route文件中揭開路由的廬山真面目每界;
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});
* 還是同樣的配方(methods)捅僵,在route類的原型上添加了get、post等方法
* 調(diào)用get等方法眨层,根據(jù)傳遞handle方法的數(shù)量循環(huán)在route的stack中添加多個層庙楚,每個方法對應(yīng)一個層
結(jié)果大概是什么樣的
注冊.png
路由的注冊到這里就解釋完成了,那么當(dāng)請求來的時候它是如何調(diào)用handler呢谐岁?
路由調(diào)用:
說到路由調(diào)用express既然是http框架,那么肯定是要先找到這段代碼榛臼;
-- application.js --
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
* 熟悉nodejs的大家肯定知道 http.createServer()方法中傳入的回調(diào)就是請求來時我們第一次調(diào)用的方法
* 那么只要找到這里傳入的this就知道是處理請求的第一層調(diào)用了伊佃;很明顯這里的this就我們的app,那app又是什么呢沛善?下邊的代碼就告訴了我們航揉;
-- express.js --
var app = function(req, res, next) {
app.handle(req, res, next);
};
* 看到這里發(fā)現(xiàn)調(diào)用app方法,會調(diào)用app.handle()
-- application.js --
app.handle = function handle(req, res, callback) {
var router = this._router;
var done = callback || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
if (!router) {
debug('no routes defined on app');
done();
return;
}
router.handle(req, res, done);
};
* 在app.handle()方法中看到會調(diào)用this._router.handle金刁,this._router相信大家都沒有忘記帅涂;
router及route中handle處理請看下圖:
分發(fā)調(diào)用-路由.png
中間件
-
中間件注冊及分發(fā)調(diào)用大體和路由一致,不過少了新建route的環(huán)節(jié)尤蛮,只存在于router.stack中媳友;
分發(fā)調(diào)用-中間件.png