Koa 是一個由 Express 原班人馬打造的新的 web 框架,Koa 自己並無捆綁任何中間件,只提供了應用(Application)、上下文(Context)、請求(Request)、響應(Response)四個模塊。本來 Express 中的路由(Router)模塊已經被移除,改成經過中間件的方式實現。相比較 Express,Koa 能讓使用者更大程度上構建個性化的應用。
Koa 是一箇中間件框架,自己沒有捆綁任何中間件。自己支持的功能並很少,功能均可以經過中間件拓展實現。經過添加不一樣的中間件,實現不一樣的需求,從而構建一個 Koa 應用。javascript
Koa 的中間件就是函數,能夠是 async 函數,或是普通函數,如下是官網的示例:前端
// async 函數 app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); // 普通函數 app.use((ctx, next) => { const start = Date.now(); return next().then(() => { const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); });
中間件能夠經過官方維護的倉庫查找獲取,也能夠根據需求編寫屬於本身的中間件。java
下面是一個的 Koa 應用,簡單演示了中間件的執行順序:git
const Koa = require('Koa'); const app = new Koa(); // 最外層的中間件 app.use(async (ctx, next) => { await console.log(`第 1 個執行`); await next(); await console.log(`第 8 個執行`); }); // 第二層中間件 app.use(async (ctx, next) => { await console.log(`第 2 個執行`); await console.log(`第 3 個執行`); await next(); await console.log(`第 6 個執行`); await console.log(`第 7 個執行`); }); // 最裏層的中間件 app.use(async (ctx, next) => { await console.log(`第 4 個執行`); ctx.body = "Hello world."; await console.log(`第 5 個執行`); }); app.listen(3000, () => { console.log(`Server port is 3000.`); })
從上面的示例中能夠看出,中間件的執行順序並非從頭至尾,而是相似於前端的事件流。事件流是先進行事件捕獲,到達目標,而後進行事件冒泡。中間件的實現過程也是同樣的,先從最外面的中間件開始執行,next()
後進入下一個中間件,一路執行到最裏面的中間件,而後再從最裏面的中間件開始往外執行。github
Koa 中間件採用的是洋蔥圈模型,每次執行下一個中間件傳入兩個參數 ctx 和 next,參數 ctx 是由 koa 傳入的封裝了 request 和 response 的變量,能夠經過它訪問 request 和 response,next 就是進入下一個要執行的中間件。web
先後端分離開發,咱們常採用 JWT 來進行身份驗證,其中 token 通常放在 HTTP 請求中的 Header Authorization 字段中,每次請求後端都要進行校驗,如 Java 的 Spring 框架能夠在過濾器中對 token 進行統一驗證,而 Koa 則經過編寫中間件來實現 token 驗證。後端
// token.js // token 中間件 module.exports = (options) => async (ctx, next) { try { // 獲取 token const token = ctx.header.authorization if (token) { try { // verify 函數驗證 token,並獲取用戶相關信息 await verify(token) } catch (err) { console.log(err) } } // 進入下一個中間件 await next() } catch (err) { console.log(err) } }
// app.js // 引入 token 中間件 const Koa = require('Koa'); const app = new Koa(); const token = require('./token') app.use(token()) app.listen(3000, () => { console.log(`Server port is 3000.`); })
日誌模塊也是線上不可缺乏的一部分,完善的日誌系統能夠幫助咱們迅速地排查出線上的問題。經過 Koa 中間件,咱們能夠實現屬於本身的日誌模塊app
// logger.js // logger 中間件 const fs = require('fs') module.exports = (options) => async (ctx, next) => { const startTime = Date.now() const requestTime = new Date() await next() const ms = Date.now() - startTime; let logout = `${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms`; // 輸出日誌文件 fs.appendFileSync('./log.txt', logout + '\n') }
// app.js // 引入 logger 中間件 const Koa = require('Koa'); const app = new Koa(); const logger = require('./logger') app.use(logger()) app.listen(3000, () => { console.log(`Server port is 3000.`); })
能夠結合 log4js 等包來記錄更詳細的日誌框架
至此,咱們已經瞭解中間件的原理,以及如何實現一個本身的中間件。前後端分離
中間件的代碼一般比較簡單,咱們能夠經過閱讀官方維護的倉庫中優秀中間件的源碼,來加深對中間件的理解和運用。