express-13 中間件

簡介

  • 從概念上講,中間件是一種功能的封裝方式,具體來講就是封裝在程序中處理HTTP請求的功能。數據庫

  • 中間件是在管道中執行的,在Express程序中,經過調用app.use向管道中插入中間件。(在Express 4.0中,中間件和路由處理器是按它們的連入順序調用的)express

  • 在管道的最後放一個「捕獲一切」請求的處理器是常見的作法,由它來處理跟前面其餘全部路由都不匹配的請求。這個中間件通常會返回狀態碼404(未找到)。json

  • 若是不調用next(),請求就在那個中間件中終止了。跨域

中間件和路由處理器

  • 路由處理器(app.get、app.post等,常常被統稱爲app.VERB)能夠被看做只處理特定HTTP謂詞(GET、POST等)的中間件; 也能夠將中間件看做能夠處理所有HTTP謂詞的路由處理器(基本上等同於app.all,能夠處理任何HTTP謂詞;瀏覽器

  • 路由處理器的第一個參數必須是路徑;中間件也能夠將路徑做爲第一個參數,但它是可選的(若是忽略這個參數,它會匹配全部路徑)。安全

  • 路由處理器和中間件的參數中都有回調函數,這個函數有2個、3個或4個參數;
    • 若是有2個或3個參數,頭兩個參數是請求和響應對象,第三個參數是next函數。
    • 若是有4個參數,它就變成了錯誤處理中間件,第一個參數變成了錯誤對象,而後依次是請求、響應和next對象。
  • 若是不調用next(),管道就會被終止,也不會再有處理器或中間件作後續處理。
    • 若是不調用next(),則應該發送一個響應到客戶端(res.send、res.json、res.render等);
    • 若是不這樣作,客戶端會被掛起並最終致使超時。
  • 若是調用了next(),通常不宜再發送響應到客戶端。
    • 若是發送了,管道中後續的中間件或路由處理器還會執行,但它們發送的任何響應都會被忽略。
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請求頭的支持,容許瀏覽器「僞裝」使用除GETPOST以外的HTTP方法。這對調試有幫助。只在編寫API時才須要。

  • query: 解析查詢字符串,並將其變成請求對象上的query屬性。這個中間件是由Express隱含連入的,因此不要本身連入它。

  • response-time: 向響應中添加X-Response-Time頭,提供以毫秒爲單位的響應時間。通常在作性能調優時才須要這個中間件。

  • static: 提供對靜態(public)文件的支持; 這個中間件能夠連入屢次,並可指定不一樣的目錄。

  • vhost: 虛擬主機(vhost),這個術語是從Apache借來的,它可以使子域名在Express中更容易管理。

相關文章
相關標籤/搜索