從概念上講,中間件是一種功能的封裝方式,具體來講就是封裝在程序中處理HTTP請求的功能。數據庫
中間件是在管道中執行的,在Express程序中,經過調用app.use向管道中插入中間件。(在Express 4.0中,中間件和路由處理器是按它們的連入順序調用的)express
在管道的最後放一個「捕獲一切」請求的處理器是常見的作法,由它來處理跟前面其餘全部路由都不匹配的請求。這個中間件通常會返回狀態碼404(未找到)。json
若是不調用next(),請求就在那個中間件中終止了。跨域
路由處理器(app.get、app.post等,常常被統稱爲app.VERB)能夠被看做只處理特定HTTP謂詞(GET、POST等)的中間件; 也能夠將中間件看做能夠處理所有HTTP謂詞的路由處理器(基本上等同於app.all,能夠處理任何HTTP謂詞;瀏覽器
路由處理器的第一個參數必須是路徑;中間件也能夠將路徑做爲第一個參數,但它是可選的(若是忽略這個參數,它會匹配全部路徑)。安全
app.use(function(req, res, next){ console.log('processing request for "' + req.url + '"....'); next(); }); app.use(function(req, res, next){ console.log('terminating request'); res.send('thanks for playing!'); // 注意,咱們沒有調用next()......這樣請求處理就終止了; //若是忽略了`res.send`,則不會有響應返回到客戶端,最終會致使客戶端超時。 }); //最後一箇中間件永遠也不會執行 app.use(function(req, res, next){ console.log('whoops, i\'ll never get called!'); });
var app = require('express')(); app.use(function(req, res, next){ console.log('\n\nALLWAYS'); next(); }); app.get('/a', function(req, res){ console.log('/a: 路由終止'); res.send('a'); }); app.get('/a', function(req, res){ console.log('/a: 永遠不會調用'); }); app.get('/b', function(req, res, next){ console.log('/b: 路由未終止'); next(); }); app.use(function(req, res, next){ console.log('SOMETIMES'); next(); }); app.get('/b', function(req, res, next){ console.log('/b (part 2): 拋出錯誤' ); throw new Error('b 失敗'); }); app.use('/b', function(err, req, res, next){ console.log('/b 檢測到錯誤並傳遞'); next(err); }); app.get('/c', function(err, req){ console.log('/c: 拋出錯誤'); throw new Error('c 失敗'); }); app.use('/c', function(err, req, res, next){ console.log('/c: 檢測到錯誤但不傳遞'); next(); }); app.use(function(err, req, res, next){ console.log('檢測到未處理的錯誤: ' + err.message); res.send('500 - 服務器錯誤'); }); app.use(function(req, res){ console.log('未處理的路由'); res.send('404 - 未找到'); }); app.listen(3000, function(){ console.log('監聽端口3000'); });
要特別注意請求/b和請求/c的差別,在這兩個實例中都有一個錯誤,但一個結果是404,另外一個是500。服務器
中間件必須是一個函數cookie
模塊能夠輸出一個函數,而這個函數又能夠直接用做中間件。網絡
//lib/tourRequiresWaiver.js模塊 module.exports = function(req,res,next){ var cart = req.session.cart; if(!cart) return next(); if(cart.some(function(item){ return item.product.requiresWaiver; })){ if(!cart.warnings) cart.warnings = []; cart.warnings.push('One or more of your selected tours' + 'requires a waiver.'); } next(); } //引入這個中間件: app.use(require('./lib/requiresWaiver.js')); //不過更常見的作法是輸出一個以中間件爲屬性的對象 module.exports = { checkWaivers: function(req, res, next){ var cart = req.session.cart; if(!cart) return next(); if(cart.some(function(i){ return i.product.requiresWaiver; })){ if(!cart.warnings) cart.warnings = []; cart.warnings.push('One or more of your selected ' + 'tours requires a waiver.'); } next(); }, checkGuestCounts: function(req, res, next){ var cart = req.session.cart; if(!cart) return next(); if(cart.some(function(item){ return item.guests > item.product.maximumGuests; })){ if(!cart.errors) cart.errors = []; cart.errors.push('One or more of your selected tours ' + 'cannot accommodate the number of guests you ' + 'have selected.'); } next(); } } //像如下這樣連入中間件 var cartValidation = require('./lib/cartValidation.js'); app.use(cartValidation.checkWaivers); app.use(cartValidation.checkGuestCounts);
在前面的例子中,咱們的中間件會用語句return next()提早終止。Express不指望中間件返回值(而且它不會用返回值作任何事情),因此這只是縮短了的next(); return;。session
Express 4.0以後,惟一保留在Express中的中間件只剩下static
basicAuth: 提供基本的訪問受權。basic-auth只提供最基本的安全,而且只能經過HTTPS使用basic-auth(不然用戶名和密碼是經過明文傳輸的)。只有在須要又快又容易的東西,而且在使用HTTPS時,才應該用basic-auth。
body-parser: 只連入json和urlencoded的便利中間件。
json: 解析JSON編碼的請求體。
urlencoded: 解析互聯網媒體類型爲application/x-www-form-urlencoded
的請求體。這是處理表單和AJAX請求最經常使用的方式;
multipart(已廢棄): 解析互聯網媒體類型爲multipart/form-data的請求體
; 應該用Busboy或Formidable代替它。
compress: 用gzip壓縮響應數據。特別使用在那些網絡比較慢或者用手機上網的狀況; 它應該在任何可能會發送響應的中間件以前被儘早連入。惟一應該出如今compress以前的中間件只有debugging或logging(它們不發送響應)。
cookie-parser: 提供對cookie的支持; app.use(require(cookie-parser)(祕鑰放在這裏)
;
cookie-session: 提供cookie存儲的會話支持。通常不推薦使用這種存儲方式的會話;必定要把它放在cookie-parser
後面連入。
express-session: 提供會話ID (存在cookie裏)的會話支持。默認存在內存裏,不適用於生產環境,而且能夠配置爲使用數據庫存儲。
csurf: 防範跨域請求僞造(CSRF)攻擊。由於它要使用會話,因此必須放在express-session
中間件後面。它目前等同於connect.csrf
中間件。惋惜簡單連入這個中間件並不能神奇地防範CSRF攻擊;
directory: 提供靜態文件的目錄清單支持。若是不須要目錄清單,則無需引入這個中間件。
errorhandler: 爲客戶端提供棧追蹤和錯誤消息;建議不要在生產環境中連入它,由於它會暴露實現細節,可能引起安全或隱私問題。
static-favicon: 提供favicon(出如今瀏覽器標題欄上的圖標)。這個中間件不是必需的,能夠簡單地在static目錄下放一個favicon.ico,但這個中間件能提高性能。若是要使用它,應該儘量地往中間件棧的上面放。
morgan: 提供自動日誌記錄支持:全部請求都會被記錄。
method-override: 提供對x-http-method-override
請求頭的支持,容許瀏覽器「僞裝」使用除GET
和POST
以外的HTTP
方法。這對調試有幫助。只在編寫API時才須要。
query: 解析查詢字符串,並將其變成請求對象上的query
屬性。這個中間件是由Express隱含連入的,因此不要本身連入它。
response-time: 向響應中添加X-Response-Time
頭,提供以毫秒爲單位的響應時間。通常在作性能調優時才須要這個中間件。
static: 提供對靜態(public)文件的支持; 這個中間件能夠連入屢次,並可指定不一樣的目錄。
vhost: 虛擬主機(vhost),這個術語是從Apache借來的,它可以使子域名在Express中更容易管理。