我實(shí)現(xiàn)的中間件模式與 Express、Koa 類(lèi)似?;谝粋€(gè) context
進(jìn)行操作,并使用這個(gè) context
作為參數(shù)按順序運(yùn)行一系列中間件。另外還傳遞一個(gè) next
函數(shù)。如果調(diào)用了這個(gè) next
函數(shù),列表中的下一個(gè)中間件將被調(diào)用;如果不調(diào)用,鏈將被中斷。此外,(與 Express 不同,但與 Koa 類(lèi)似)中間件可以是 async
函數(shù)或返回一個(gè) Promise。
準(zhǔn)備工作
首先描述中間件:
/**
* 傳遞給中間件的 'next' 函數(shù)
*/
type Next = () => void | Promise<void>;
/**
* 一個(gè)中間件
*/
type Middleware<T> =
(context: T, next: Next) => Promise<void> | void;
Middleware
是實(shí)際的異步/非異步中間件函數(shù)。我為 Next
定義了一個(gè)類(lèi)型,這樣就不需要多次編寫(xiě)它了。
如何使用它
假設(shè)我們有一個(gè)“應(yīng)用程序”、一組中間件和一個(gè)我們想要操作的上下文。通過(guò)以下代碼表示:
/**
* 應(yīng)用程序的上下文類(lèi)型。
* 在 'koa' 中,這個(gè)對(duì)象將持有對(duì) 'request' 和 'response' 的引用,
* 但我們的上下文只有一個(gè)屬性。
*/
type MyContext = {
a: number;
}
/**
* 創(chuàng)建應(yīng)用程序?qū)ο?br style="-webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"> */
const app = new MwDispatcher<MyContext>();
/**
* 一個(gè)中間件
*/
app.use((context: MyContext, next: Next) => {
context.a += 1;
return next();
});
/**
* 一個(gè)異步中間件
*/
app.use(async (context: MyContext, next: Next) => {
// 等待 2 秒
await new Promise(res => setTimeout(res, 2000));
context.a += 2;
return next();
});
運(yùn)行這個(gè)應(yīng)用程序
const context: MyContext = {
a: 0,
}
await app.dispatch(context);
console.log(context.a); // 應(yīng)該輸出 3
實(shí)現(xiàn)
實(shí)現(xiàn)這一切的代碼非常簡(jiǎn)潔:
/**
* 中間件容器和調(diào)用器
*/
class MwDispatcher<T> {
middlewares: Middleware<T>[];
constructor() {
this.middlewares = [];
}
/**
* 添加一個(gè)中間件函數(shù)。
*/
use(...mw: Middleware<T>[]): void {
this.middlewares.push(...mw);
}
/**
* 在給定的上下文中按添加順序執(zhí)行中間件鏈。
*/
dispatch(context: T): Promise<void> {
return invokeMiddlewares(context, this.middlewares)
}
}
/**
* 在上下文中調(diào)用中間件鏈的輔助函數(shù)。
*/
async function invokeMiddlewares<T>(context: T, middlewares: Middleware<T>[]): Promise<void> {
if (!middlewares.length) return;
const mw = middlewares[0];
return mw(context, async () => {
await invokeMiddlewares(context, middlewares.slice(1));
})
}
該文章在 2024/11/16 8:51:53 編輯過(guò)