學習Koa – 讓我們寫一箇中介軟體

WU_CHONG發表於2019-03-04

中介軟體寫法

根據我們前文的分析,中介軟體通常為以下形式:

async (ctx, next) => {....}
複製程式碼

一般來說中介軟體有自己的配置,所以我們總結出來一種通用的中介軟體寫法,我們通過傳入配置的方式可以返回根據配置定製的中介軟體:

// 通用中介軟體寫法
module.exports = function(options) {
  // 配置處理
  
  return aysnc (ctx, next) => {
    // 中介軟體邏輯...
  }
}
複製程式碼

使用方式:

app.use(middleware(options)) // middleware(options)返回一箇中介軟體
複製程式碼

除此之外,還有另一種中介軟體寫法。正如前文分析的一樣,koa-compose可以將多箇中介軟體合成一箇中介軟體,那麼我們也可將有關聯的中介軟體合成一個大的中介軟體,寫法如下:

app.use(compose([middleware1, middleware2, ...]))
複製程式碼

常見中介軟體

Koa是基於中介軟體模式的,但是框架本身並不包含任何中介軟體。在實際的開發當中,我們可以通過組合中介軟體進行功能的實現。Koa中常見的中介軟體如下:

  • Koa-router:Koa-router的實現機制與koa本身的實現類似,都是分為兩個階段,初始化階段和執行階段。初始化階段主要涉及到路由的註冊,執行階段為尋找到匹配的路由,然後使用compose函式合成一箇中介軟體,執行該中介軟體。一個例子說明:
var Koa = require(`koa`);
var Router = require(`koa-router`);

var app = new Koa();
var router = new Router();

router.get(`/`, (ctx, next) => {}); // 註冊路由

app.use(router.routes()) // 返回匹配路由的複合中介軟體
  .use(router.allowedMethods());
複製程式碼

感興趣的也可以參看原始碼,簡單易懂

  • Koa-bodyparser
    前文中講過,node只會對header進行解析,header解析之後便傳送一個request事件,body資料獲取需要自己對req(incomingMessage)上的data和end事件進行監聽。
const http = require(`http`);

http.createServer((req , res)=>{
  console.log(`request here.`);
  let data = ``;
  req.on(`data`,(chunk)=>{
    data += chunk;
  });
  req.on(`end`,()=>{
    console.log(`data:`,data);
  });
}).listen(3000,`127.0.0.1`);
複製程式碼

bodyparser中介軟體的作用就是獲取body資料並按照想要的格式進行解析,將解析後的資料賦值給ctx.body。

app.use(bodyParser(options));
複製程式碼
  • Koa-static:為靜態資源訪問建立一個伺服器,根據url訪問對應的資料夾、檔案
  • Koa-ejs:使用ejs渲染服務端模板

更多中介軟體請參考koa官方清單:https://github.com/koajs/koa/wiki

自己寫中介軟體

說了這麼多中介軟體,我們以自己寫一箇中介軟體作為該篇結束。以下示例為一個現實中的例子,做了一部分精簡。問題的背景為,網站經常被某些ip攻擊,影響業務正常執行,於是做了一箇中介軟體進行ip的過濾,對於ip黑名單上的ip一律拒絕進一步處理請求。希望通過這個示例,能夠使大家進一步瞭解中介軟體的作用。

/*
** iplimiter: ip過濾中介軟體,對於在ip黑名單上的ip直接返回,請求不再進行下去
** ip_blacklist: Array, example: [`192.123.12.11`]
*/
module.exports = function(ip_blacklist) {
  return async (ctx, next) => {
    if(!Array.isArray(configs) && configs.length) {
      let ip = ctx.request.headers[`x-real-ip`] || `` //獲取客戶端ip,由於使用nginx作為負載均衡,所以獲取ip的方式可通過x-real-ip欄位
      if(ip && ip_blacklist.indexOf(ip) !== -1) {
        await next()
      } else {
        return res.end(`ip restricted`)
      }
    } else {
      await next()
    }
  }
}
複製程式碼

也請大家關注我的知乎專欄,漲漲粉:知乎專欄:前端思考

相關文章