50行程式碼學會koa2中介軟體原理
Koa 是 nodejs 開發的下一代 web 開發框架,可參考 。說是“下一代”,其實在實際開發中早就用在專案中了。特別是 nodejs 新版本開始正式支援 async/await
語法之後,Koa2 正在被大量使用。
關於 Koa2 的基本使用和中介軟體機制的使用,大家可以去官網查閱和學習,本文主要講解 Koa2 的中介軟體原理 —— 而且是透過非常簡短的 50 行程式碼。程式碼寫完之後,要能實現官網中的一段中介軟體示例,如下:
const Koa = require('koa');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
// response
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
這段程式碼的意圖是:第一,記錄服務開始的時間戳;第二,返回 hello word
;第三,記錄返回之後的時間戳,然後算出時間間隔並列印。接下來我們就透過這段程式碼,分析一下 Koa2 的中介軟體實現原理。
上述示例程式碼中,有三個 app.use
,從使用角度分析它的用意,其實就是註冊中介軟體函式。因此,首先可以這樣定義我們自己的 Koa 程式碼,新建一個 like-koa2.js
,開始編寫:
class LikeKoa2 {
constructor() {
this.middlewareList = []
}
// 核心方法
use(fn) {
this.middlewareList.push(fn)
return this
}
}
首先定一個類,然後建構函式中初始化 middelwareList
陣列,用以儲存所有的中介軟體函式。use
中接收中介軟體函式,然後放到 middelwareList
陣列中,就算是註冊完成。最後 return this
是為了能實現鏈式操作,例如 app.use(fn1).use(fn2).use(fn3)
,實際是否這樣用看自己的需求。
示例程式碼的最後使用 app.listen(3000)
啟動服務監聽,可以轉化為 nodejs 原生的 http 處理方式。程式碼如下:
const http = require('http');
class LikeKoa2 {
constructor() {
this.middlewareList = []
}
// 核心方法
use(fn) {
this.middlewareList.push(fn)
return this
}
// 將 req res 組合成為 ctx
createContext(req, res) {
// 簡單模擬 koa 的 ctx ,不管細節了
const ctx = {
req,
res
}
return ctx
}
// 生成 http.createServer 需要的回撥函式
callback() {
return (req, res) => {
const ctx = this.createContext(req, res)
}
}
listen(...args) {
const server = http.createServer(this.callback())
return server.listen(...args);
}
}
需要簡單解釋兩點。第一,nodejs 原生的 http.createServer
需要傳入一個回撥函式,在 callback()
中返回。第二,示例程式碼中中介軟體函式的第一個引數都是 ctx
,其實可以簡答理解為 res
和 req
的集合,透過 createContext
合併一下即可。
上述程式碼,獲取到 ctx
之後,並沒有做下一步處理,下文會繼續解釋。
上文一開始使用 use
來註冊中介軟體,再就是用 listen
去啟動並監聽服務,即剛開始就直接結束了。其實中間漏下很重要的一個步驟 —— 中介軟體組合,即如何讓中間有 next
機制,將中介軟體一個一個的串起來。
Koa2 中透過一個 compose
函式來組合中介軟體,以及實現了 next
機制。具體程式碼有點繞,不太好解釋,我儘量用通俗的語言、結合程式碼註釋,解釋清楚。先看程式碼:
// 傳入中介軟體列表
function compose(middlewareList) {
// 返回一個函式,接收 ctx (即 res 和 req 的組合)—— 記住了,下文要用
return function (ctx) {
// 定義一個派發器,這裡面就實現了 next 機制
function dispatch(i) {
// 獲取當前中介軟體
const fn = middlewareList[i]
try {
return Promise.resolve(
// 透過 i + 1 獲取下一個中介軟體,傳遞給 next 引數
fn(ctx, dispatch.bind(null, i + 1))
)
} catch (err) {
return Promise.reject(err)
}
}
// 開始派發第一個中介軟體
return dispatch(0)
}
}
我們按步驟解釋一下上述程式碼。
- 第一,定義
compose
函式,並接收中介軟體列表。 - 第二,
compose
函式中返回一個函式,該函式接收ctx
,下文會用這個返回的函式。 - 第三,再往內部,定義了一個
dispatch
函式,就是一箇中介軟體的派發器,引數i
就代表派發第幾個中介軟體。執行dispatch(0)
就是開發派發第一個中介軟體。 - 第四,派發器內部,透過
i
獲取當前的中介軟體,然後執行。執行時傳入的第一個引數是ctx
,第二個引數是dispatch.bind(null, i + 1)
即下一個中介軟體函式 —— 也正好對應到示例程式碼中中介軟體的next
引數。 - 用
Promise.resolve
封裝起來,是為了保證函式執行的結果必須是Promise
型別。
就是這麼多步驟,感覺自己已經解釋的很詳細了,但確實比較繞。如果有看不懂的同學,我建議多看幾遍,或者自己親自動手寫幾遍,熟練了也就掌握了。
有了 compose
之後,callback
即可被完善起來,相關程式碼(並不是全部的程式碼)如下,其中新增的 handleRequest
看看註釋應該也能明白了。
// 處理中介軟體的 http 請求
handleRequest(ctx, middleWare) {
// 這個 middleWare 就是 compose 函式返回的 fn
// 執行 middleWare(ctx) 其實就是執行中介軟體函式,然後再用 Promise.resolve 封裝並返回
return middleWare(ctx)
}
callback() {
const fn = compose(this.middlewareList)
return (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
}
以上就是分析的全部內容,下面列出完整的程式碼,但希望大家不要直接複製,而是自己親手寫出來。
const http = require('http');
// 組合中介軟體
function compose(middlewareList) {
return function (ctx) {
function dispatch(i) {
const fn = middlewareList[i]
try {
return Promise.resolve(
fn(ctx, dispatch.bind(null, i + 1))
)
} catch (err) {
return Promise.reject(err)
}
}
return dispatch(0)
}
}
class LikeKoa2 {
constructor() {
this.middlewareList = []
}
// 核心方法
use(fn) {
this.middlewareList.push(fn)
return this
}
// 處理中介軟體的 http 請求
handleRequest(ctx, middleWare) {
// 這個 middleWare 就是 compose 函式返回的 fn
// 執行 middleWare(ctx) 其實就是執行中介軟體函式,然後再用 Promise.resolve 封裝並返回
return middleWare(ctx)
}
// 將 req res 組合成為 ctx
createContext(req, res) {
// 簡單模擬 koa 的 ctx ,不管細節了
const ctx = {
req,
res
}
return ctx
}
callback() {
const fn = compose(this.middlewareList)
return (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
}
listen(...args) {
const server = http.createServer(this.callback())
return server.listen(...args);
}
}
module.exports = LikeKoa2
標題說“50行程式碼”其實有點誇張了,因為算上程式碼的空行和註釋,一共 60 多行 —— 空行和註釋也是程式碼的一部分嘛!
新建一個 test.js
然後開始編寫:
const Koa = require('./like-koa2');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
await next();
const rt = ctx['X-Response-Time'];
console.log(`${ctx.req.method} ${ctx.req.url} - ${rt}`);
});
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx['X-Response-Time'] = `${ms}ms`;
});
// response
app.use(async ctx => {
ctx.res.end('hello world')
});
app.listen(8000);
請大家注意,我們這裡的程式碼示例和一開始官網的示例是有一些區別的,例如這裡的 ctx['X-Response-Time']
和官網示例的 ctx.set('X-Response-Time', ...)
。這是因為我們的 ctx
是簡單的將 res
和 req
拼接而成,而 Koa2 中的 ctx
還做了一些 API 的擴充套件和處理,但是這並不是我們理解中介軟體原理的障礙,因此可以忽略。
最後,node test.js
執行起來,然後瀏覽器訪問 ,看控制檯能否列印出日誌記錄。提示,nodejs 版本必須 >= 8.0 。
·····································
大家可以關注一下課程:
【新課】
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3016/viewspace-2821800/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Koa2 中介軟體原理解析 —— 看了就會寫
- koa2 總體流程原理淺析(二) 之 中介軟體原理
- koa2中介軟體koa和koa-compose原始碼分析原理(一)原始碼
- 從原始碼理解Redux和Koa2的中介軟體機制原始碼Redux
- KOA2 手寫中介軟體 (裝飾器模式)模式
- 一文學會Vue中介軟體管道Vue
- Express中介軟體原理詳解Express
- koa2第二篇: 圖解中介軟體原始碼執行過程圖解原始碼
- KOA2 compose 串聯中介軟體實現(洋蔥模型)模型
- Express 中介軟體 getcookies 後門程式碼分析ExpressCookie
- 學習 redux 原始碼整體架構,深入理解 redux 及其中介軟體原理Redux原始碼架構
- Redux 中介軟體的實現原理Redux
- 理解Laravel中介軟體核心實現原理Laravel
- Day14 session原理和中介軟體Session
- 基於nodejs koa2的解決跨域中介軟體設計NodeJS跨域
- Redis中介軟體與Web中介軟體RedisWeb
- MQ系列:訊息中介軟體執行原理MQ
- 逐行分析Koa v1 中介軟體原理
- 你知道 koa 中介軟體執行原理嗎?
- 中介軟體之訊息中介軟體-pulsar
- 十分鐘學會用 Go 編寫 Web 中介軟體GoWeb
- Redux Middleware中介軟體原始碼 分析Redux原始碼
- node JS 中 express 中介軟體實現原理分析JSExpress
- 中介軟體漏洞攻防學習總結
- Django學習筆記(15)——中介軟體Django筆記
- 分散式事務中介軟體Seata的設計原理分散式
- ASP.NET Core中的中介軟體及其工作原理ASP.NET
- 中介軟體漏洞
- 中介軟體-NginxNginx
- ThinkPHP 中介軟體PHP
- MySQL中介軟體MySql
- django中介軟體Django
- golang 中介軟體Golang
- 中介軟體整理
- redux中介軟體Redux
- Laravel 中介軟體Laravel
- 中介軟體(middleware)
- Django——中介軟體Django