據(jù)的代碼復(fù)用技巧)
Inversify-Express-Utils 控制器繼承指南基于原型鏈元數(shù)據(jù)的代碼復(fù)用技巧【免費(fèi)下載鏈接】inversify-express-utilsSome utilities for the development of Express application with InversifyJS項目地址: https://gitcode.com/gh_mirrors/in/inversify-express-utilsInversify-Express-Utils是 InversifyJS 生態(tài)中用于構(gòu)建 Express 應(yīng)用的官方工具庫它通過裝飾器將 TypeScript 類映射為 HTTP 路由。本指南聚焦一個新手最關(guān)心的進(jìn)階能力——控制器繼承如何讓子類控制器自動復(fù)用父類中定義的 API 方法并解析其背后的原型鏈元數(shù)據(jù)查找機(jī)制。為什么需要控制器繼承在真實的業(yè)務(wù)項目中你會經(jīng)常遇到這樣的場景用戶、訂單、商品等多個資源的 CRUD 接口高度相似只有業(yè)務(wù)細(xì)節(jié)不同你希望一套通用的列表、詳情、創(chuàng)建邏輯能被多個控制器共享修改一處全局生效避免復(fù)制粘貼帶來的維護(hù)災(zāi)難。Inversify-Express-Utils 從設(shè)計上就支持這種繼承——這是它相比手寫 Express 路由的一大優(yōu)勢路由元數(shù)據(jù)不僅存在于類自身還能沿著JavaScript 原型鏈向上查找。核心原理元數(shù)據(jù)如何沿原型鏈查找理解繼承之前先搞清楚裝飾器做了什么。裝飾器把路由信息寫成元數(shù)據(jù)當(dāng)你使用controller和httpGet等裝飾器時框架并不會立即創(chuàng)建路由而是把方法名、HTTP 動詞、路徑等信息以**元數(shù)據(jù)metadata**的形式記錄在類構(gòu)造函數(shù)上。這個邏輯位于 decorators.ts 中的httpMethod函數(shù)它會把每個被裝飾方法的描述追加到當(dāng)前類的元數(shù)據(jù)列表里。controller裝飾器還會順手為類附加injectable()讓控制器自動成為可注入的依賴無需手動綁定。關(guān)鍵一步getOwnMetadata 與 getMetadata 的配合真正讓繼承成立的是 utils.ts 中的getControllerMethodMetadata函數(shù)。它的查找策略非常巧妙先用Reflect.getOwnMetadata讀取子類自己的方法元數(shù)據(jù)再用Reflect.getMetadata讀取父類原型上的元數(shù)據(jù)通過Reflect.getPrototypeOf(constructor)獲取父構(gòu)造函數(shù)兩者都存在時先放子類、后放父類進(jìn)行拼接合并。getControllerParameterMetadata對參數(shù)元數(shù)據(jù)如requestParam、requestBody也采用同樣的原型鏈合并策略。 一句話總結(jié)getOwnMetadata只看本類getMetadata會沿原型鏈向上找。框架刻意組合使用這兩個 API才讓父類里的路由方法能被子類免費(fèi)繼承。三步實踐寫出可繼承的控制器下面用項目中 controller_inheritance.test.ts 的真實示例來說明。第 1 步定義泛型基類控制器用一個泛型類承載所有資源通用的 CRUD 方法不要給它加controller裝飾器它不是具體控制器injectable() class GenericControllerT { httpGet(/) public get() { return { status: BASE GET! }; } httpPost(/) public post(requestBody() body: T) { return { args: body, status: BASE POST! }; } // 可繼續(xù)定義 httpPut、httpDelete 等通用方法 }第 2 步業(yè)務(wù)控制器繼承基類具體控制器用controller指定自己的路由前綴只寫差異化的方法controller(/api/v1/movies) class MoviesController extends GenericControllerMovie { httpDelete(/:movieId/actors/:actorId) public deleteActor( requestParam(movieId) movieId: string, requestParam(actorId) actorId: string, ) { return { status: DERIVED DELETE ACTOR! ${movieId} ${actorId} }; } }啟動后/api/v1/movies同時擁有基類的GET /、POST /以及自己新增的刪除演員接口。你可以復(fù)制同樣的模式創(chuàng)建/api/v1/movies2、/api/v1/movies3等多個控制器——這正是測試文件中驗證的行為每個繼承控制器都自動獲得父類全部路由。第 3 步常規(guī)注冊即可啟動繼承場景不需要任何特殊注冊代碼照常交給InversifyExpressServerconst app new InversifyExpressServer(container); app.setConfig((a) { a.use(json()); a.use(urlencoded({ extended: true })); }); const server app.build();另一個繼承模式BaseHttpController除了泛型基類項目還提供了官方的 base_http_controller.ts它是另一條繼承復(fù)用路線——響應(yīng)方法復(fù)用。BaseHttpController注入httpContext并提供了一整套可復(fù)用的響應(yīng)方法方法用途this.ok(content)返回 200 及內(nèi)容this.created(location, content)資源創(chuàng)建成功201this.badRequest(message)參數(shù)錯誤400this.notFound()資源不存在404this.json(content, statusCode)自定義狀態(tài)碼的 JSONthis.redirect(uri)重定向this.stream(...)流式響應(yīng)繼承它的最大好處是可測試性控制器返回的是結(jié)果對象而非直接操作res單元測試時無需 mock 整個 HTTP 響應(yīng)。多個業(yè)務(wù)控制器共享這一個基類響應(yīng)行為自然保持一致。常見陷阱與注意事項??只繼承直接父類的元數(shù)據(jù)元數(shù)據(jù)查找是一層一層沿原型鏈進(jìn)行的每層讀取getPrototypeOf的結(jié)果多層繼承可以工作但越深越難排查建議控制在兩層以內(nèi)。??基類不要加controller只有加了controller的類才會被登記進(jìn)全局控制器列表注冊邏輯見 decorators.ts元數(shù)據(jù)掛在Reflect對象本身。基類若誤加裝飾器會以抽象類身份被注冊成控制器。??元數(shù)據(jù)只在類被 import 時生成控制器文件必須被導(dǎo)入至少一次否則元數(shù)據(jù)從未產(chǎn)生路由也就不會出現(xiàn)。這是 README 中明確強(qiáng)調(diào)的坑繼承時同樣適用——確保基類與子類文件都在啟動入口被引入。??同名方法會被靜默雙份注冊子類覆蓋父類同名方法時原型鏈查找會讓兩個版本的方法元數(shù)據(jù)都進(jìn)入合并結(jié)果路由行為可能不符合預(yù)期。想讓子類接管某個路由請讓路徑或方法名有區(qū)分度。??測試時的清理開發(fā)測試用例時可用 utils.ts 導(dǎo)出的cleanUpMetadata()重置全局元數(shù)據(jù)避免多個用例之間相互污染項目測試的beforeEach中就是這么做的。關(guān)鍵文件速查文件路徑作用src/decorators.tscontroller、httpGet等裝飾器與元數(shù)據(jù)寫入src/utils.ts原型鏈元數(shù)據(jù)合并的核心實現(xiàn)src/base_http_controller.ts官方基類提供可復(fù)用響應(yīng)方法src/server.tsInversifyExpressServer掃描元數(shù)據(jù)生成路由src/test/features/controller_inheritance.test.ts控制器繼承的完整端到端測試小結(jié)Inversify-Express-Utils 的控制器繼承本質(zhì)是元數(shù)據(jù) 原型鏈的組合拳裝飾器把路由信息寫成元數(shù)據(jù)框架在解析時先用getOwnMetadata取子類自身的定義再用getMetadata沿原型鏈補(bǔ)齊父類的定義。掌握這套機(jī)制后你可以用泛型基類抹平重復(fù)的 CRUD 代碼用BaseHttpController統(tǒng)一響應(yīng)風(fēng)格——代碼更少行為更一致維護(hù)成本大幅下降。【免費(fèi)下載鏈接】inversify-express-utilsSome utilities for the development of Express application with InversifyJS項目地址: https://gitcode.com/gh_mirrors/in/inversify-express-utils創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考