日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
如何讓你的Express飛起來(lái)

接下來(lái)本文的重心將圍繞 裝飾器 的應(yīng)用展開(kāi),不過(guò)在分析裝飾器在 OvernightJS 的應(yīng)用之前,阿寶哥先來(lái)簡(jiǎn)單介紹一下 OvernightJS。

目前成都創(chuàng)新互聯(lián)已為上千多家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機(jī)、網(wǎng)站托管、服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計(jì)、江州網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

一、OvernightJS 簡(jiǎn)介 

 
 
 
 
  1. TypeScript decorators for the ExpressJS Server.

OvernightJS 是一個(gè)簡(jiǎn)單的庫(kù),用于為要調(diào)用 Express 路由的方法添加 TypeScript 裝飾器。此外,該項(xiàng)目還包含了用于管理 json-web-token 和打印日志的包。

1.1 OvernightJS 特性
OvernightJS 并不是為了替代 Express,如果你之前已經(jīng)掌握了 Express,那你就可以快速地學(xué)會(huì)它。OvernightJS 為開(kāi)發(fā)者提供了以下特性:

  • 使用 @Controller 裝飾器定義基礎(chǔ)路由;
  • 提供了把類(lèi)方法轉(zhuǎn)化為 Express 路由的裝飾器(比如 @Get,@Put,@Post,@Delete);
  • 提供了用于處理中間件的 @Middleware 和 @ClassMiddleware 裝飾器;
  • 提供了用于處理異常的 @ErrorMiddleware 裝飾器;
  • 提供了 @Wrapper 和 @ClassWrapper 裝飾器用于包裝函數(shù);
  • 通過(guò) @ChildControllers 裝飾器支持子控制器。

出于篇幅考慮,阿寶哥只介紹了 OvernightJS 與裝飾器相關(guān)的部分特性。了解完這些特性,我們來(lái)快速體驗(yàn)一下 OvernightJS。

1.2 OvernightJS 入門(mén)
1.2.1 初始化項(xiàng)目
首先新建一個(gè) overnight-quickstart 項(xiàng)目,然后使用 npm init -y 命令初始化項(xiàng)目,然后在命令行中輸入以下命令來(lái)安裝項(xiàng)目依賴(lài)包:

 
 
 
 
  1. $ npm i @overnightjs/core express -S

在 Express 項(xiàng)目中要集成 TypeScript 很簡(jiǎn)單,只需安裝 typescript 這個(gè)包就可以了。但為了在開(kāi)發(fā)階段能夠在命令行直接運(yùn)行使用 TypeScript 開(kāi)發(fā)的服務(wù)器,我們還需要安裝 ts-node 這個(gè)包。要安裝這兩個(gè)包,我們只需在命令行中輸入以下命令:

 
 
 
 
  1. $ npm i typescript ts-node -D

1.2.2 為 Node.js 和 Express 安裝聲明文件
聲明文件是預(yù)定義的模塊,用于告訴 TypeScript 編譯器的 JavaScript 值的形狀。類(lèi)型聲明通常包含在擴(kuò)展名為 .d.ts 的文件中。這些聲明文件可用于所有最初用 JavaScript 而非 TypeScript 編寫(xiě)的庫(kù)。

幸運(yùn)的是,我們不需要重頭開(kāi)始為 Node.js 和 Express 定義聲明文件,因?yàn)樵?Github 上有一個(gè)名為 DefinitelyTyped 項(xiàng)目已經(jīng)為我們提供了現(xiàn)成的聲明文件。

要安裝 Node.js 和 Express 對(duì)應(yīng)的聲明文件,我們只需要在命令行執(zhí)行以下命令就可以了:

 
 
 
 
  1. $ npm i @types/node @types/express -D

該命令成功執(zhí)行之后,package.json 中的 devDependencies 屬性就會(huì)新增 Node.js 和 Express 對(duì)應(yīng)的依賴(lài)包版本信息:

 
 
 
 
  1. {
  2.   "devDependencies": {
  3.      "@types/express": "^4.17.8",
  4.      "@types/node": "^14.11.2",
  5.      "ts-node": "^9.0.0",
  6.      "typescript": "^4.0.3"
  7.   }
  8. }

1.2.3 初始化 TypeScript 配置文件
為了能夠靈活地配置 TypeScript 項(xiàng)目,我們還需要為本項(xiàng)目生成 TypeScript 配置文件,在命令行輸入 tsc --init 之后,項(xiàng)目中就會(huì)自動(dòng)創(chuàng)建一個(gè) tsconfig.json 的文件。對(duì)于本項(xiàng)目來(lái)說(shuō),我們將使用以下配置項(xiàng):

 
 
 
 
  1. {
  2.   "compilerOptions": {
  3.     "target": "es6",
  4.     "module": "commonjs",
  5.     "rootDir": "./src",
  6.     "outDir": "./build",
  7.     "esModuleInterop": true,
  8.     "experimentalDecorators": true,
  9.     "strict": true
  10.   }
  11. }

1.2.4 創(chuàng)建簡(jiǎn)單的 Web 服務(wù)器
在創(chuàng)建簡(jiǎn)單的 Web 服務(wù)器之前,我們先來(lái)初始化項(xiàng)目的目錄結(jié)構(gòu)。首先在項(xiàng)目的根目錄下創(chuàng)建一個(gè) src 目錄及 controllers 子目錄:

 
 
 
 
  1. ├── src
  2. │   ├── controllers
  3. │   │   └── UserController.ts
  4. │   └── index.ts

接著新建 UserController.ts 和 index.ts 這兩個(gè)文件并分別輸入以下內(nèi)容:

UserController.ts

 
 
 
 
  1. import { Controller, Get } from "@overnightjs/core";
  2. import { Request, Response } from "express";
  3. @Controller("api/users")
  4. export class UserController {
  5.   @Get("")
  6.   private getAll(req: Request, res: Response) {
  7.     return res.status(200).json({
  8.       message: "成功獲取所有用戶(hù)",
  9.     });
  10.   }
  11. }

index.ts

 
 
 
 
  1. import { Server } from "@overnightjs/core";
  2. import { UserController } from "./controllers/UserController";
  3. const PORT = 3000;
  4. export class SampleServer extends Server {
  5.   constructor() {
  6.     super(process.env.NODE_ENV === "development");
  7.     this.setupControllers();
  8.   }
  9.   private setupControllers(): void {
  10.     const userController = new UserController();
  11.     super.addControllers([userController]);
  12.   }
  13.   public start(port: number): void {
  14.     this.app.listen(port, () => {
  15.       console.log(`?[server]: Server is running at http://localhost:${PORT}`);
  16.     });
  17.   }
  18. }
  19. const sampleServer = new SampleServer();
  20. sampleServer.start(PORT);

完成上述步驟之后,我們?cè)陧?xiàng)目的 package.json 中添加一個(gè) start 命令來(lái)啟動(dòng)項(xiàng)目:

 
 
 
 
  1. {
  2.   "scripts": {
  3.     "start": "ts-node ./src/index.ts"
  4.   },
  5. }

添加完 start 命令,我們就可以在命令行中通過(guò) npm start 來(lái)啟動(dòng) Web 服務(wù)器了。當(dāng)服務(wù)器成功啟動(dòng)之后,命令行會(huì)輸出以下消息:

 
 
 
 
  1. > ts-node ./src/index.ts
  2. ?[server]: Server is running at http://localhost:3000

接著我們打開(kāi)瀏覽器訪問(wèn) http://localhost:3000/api/users 這個(gè)地址,你就會(huì)看到 {"message":"成功獲取所有用戶(hù)"} 這個(gè)信息。

1.2.5 安裝 nodemon
為了方便后續(xù)的開(kāi)發(fā),我們還需要安裝一個(gè)第三方包 nodemon。對(duì)于寫(xiě)過(guò) Node.js 應(yīng)用的小伙伴來(lái)說(shuō),對(duì) nodemon 這個(gè)包應(yīng)該不會(huì)陌生。nodemon 這個(gè)包會(huì)自動(dòng)檢測(cè)目錄中文件的更改,當(dāng)發(fā)現(xiàn)文件異動(dòng)時(shí),會(huì)自動(dòng)重啟 Node.js 應(yīng)用程序。

同樣,我們?cè)诿钚袌?zhí)行以下命令來(lái)安裝它:

 
 
 
 
  1. $ npm i nodemon -D

安裝完成后,我們需要更新一下前面已經(jīng)創(chuàng)建的 start 命令:

 
 
 
 
  1. {
  2.   "scripts": {
  3.     "start": "nodemon ./src/index.ts"
  4.   }
  5. }

好的,現(xiàn)在我們已經(jīng)知道如何使用 OvernightJS 來(lái)開(kāi)發(fā)一個(gè)簡(jiǎn)單的 Web 服務(wù)器。接下來(lái),阿寶哥將帶大家一起來(lái)分析 OvernightJS 是如何使用 TypeScript 裝飾器實(shí)現(xiàn)上述的功能。

二、OvernightJS 原理分析
在分析前面示例中 @Controller 和 @Get 裝飾器原理前,我們先來(lái)看一下直接使用 Express 如何實(shí)現(xiàn)同樣的功能:

 
 
 
 
  1. import express, { Router, Request, Response } from "express";
  2. const app = express();
  3. const PORT = 3000;
  4. class UserController {
  5.   public getAll(req: Request, res: Response) {
  6.     return res.status(200).json({
  7.       message: "成功獲取所有用戶(hù)",
  8.     });
  9.   }
  10. }
  11. const userRouter = Router();
  12. const userCtrl = new UserController();
  13. userRouter.get("/", userCtrl.getAll);
  14. app.use("/api/users", userRouter);
  15. app.listen(PORT, () => {
  16.   console.log(`?[server]: Server is running at http://localhost:${PORT}`);
  17. });

在以上代碼中,我們先通過(guò)調(diào)用 Router 方法創(chuàng)建了一個(gè) userRouter 對(duì)象,然后進(jìn)行相關(guān)路由的配置,接著使用 app.use 方法應(yīng)用 userRouter 路由。下面我們用一張圖來(lái)直觀感受一下 OvernightJS 與 Express 在使用上的差異:

通過(guò)以上對(duì)比可知,利用 OvernightJS 提供的裝飾器,可以讓我們開(kāi)發(fā)起來(lái)更加便捷。但大家要記住 OvernightJS 底層還是基于 Express,其內(nèi)部最終還是通過(guò) Express 提供的 API 來(lái)處理路由。

接下來(lái)為了能更好理解后續(xù)的內(nèi)容,我們先來(lái)簡(jiǎn)單回顧一下 TypeScript 裝飾器。

2.1 TypeScript 裝飾器簡(jiǎn)介
裝飾器是一個(gè)表達(dá)式,該表達(dá)式執(zhí)行后,會(huì)返回一個(gè)函數(shù)。在 TypeScript 中裝飾器可以分為以下 4 類(lèi):

需要注意的是,若要啟用實(shí)驗(yàn)性的裝飾器特性,你必須在命令行或 tsconfig.json 里啟用 experimentalDecorators 編譯器選項(xiàng):

命令行:

 
 
 
 
  1. tsc --target ES5 --experimentalDecorators

tsconfig.json:

 
 
 
 
  1. {
  2.   "compilerOptions": {
  3.      "experimentalDecorators": true
  4.    }
  5. }

了解完 TypeScript 裝飾器的分類(lèi),我們來(lái)開(kāi)始分析 OvernightJS 框架中提供的裝飾器。

2.2 @Controller 裝飾器
在前面創(chuàng)建的簡(jiǎn)單 Web 服務(wù)器中,我們通過(guò)以下方式來(lái)使用 @Controller 裝飾器:

 
 
 
 
  1. @Controller("api/users")
  2. export class UserController {}

很明顯該裝飾器應(yīng)用在 UserController 類(lèi)上,它屬于類(lèi)裝飾器。OvernightJS 的項(xiàng)目結(jié)構(gòu)很簡(jiǎn)單,我們可以很容易找到 @Controller 裝飾器的定義:

 
 
 
 
  1. // src/core/lib/decorators/class.ts
  2. export function Controller(path: string): ClassDecorator {
  3.   return (target: TFunction): void => {
  4.     addBasePathToClassMetadata(target.prototype, "/" + path);
  5.   };
  6. }

通過(guò)觀察以上代碼可知,Controller 函數(shù)是一個(gè)裝飾器工廠,即調(diào)用該工廠方法之后會(huì)返回一個(gè) ClassDecorator 對(duì)象。在 ClassDecorator 內(nèi)部,會(huì)繼續(xù)調(diào)用 addBasePathToClassMetadata 方法,把基礎(chǔ)路徑添加到類(lèi)的元數(shù)據(jù)中:

 
 
 
 
  1. // src/core/lib/decorators/class.ts
  2. export function addBasePathToClassMetadata(target: Object, basePath: string): void {
  3.   let metadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, target);
  4.   if (!metadata) {
  5.       metadata = {};
  6.   }
  7.   metadata.basePath = basePath;
  8.   Reflect.defineMetadata(classMetadataKey, metadata, target);
  9. }

addBasePathToClassMetadata 函數(shù)的實(shí)現(xiàn)很簡(jiǎn)單,主要是利用 Reflect API 實(shí)現(xiàn)元數(shù)據(jù)的存取操作。在以上代碼中,會(huì)先獲取 target 對(duì)象上已保存的 metadata 對(duì)象,如果不存在的話(huà),會(huì)創(chuàng)建一個(gè)空的對(duì)象,然后把參數(shù) basePath 的值添加該對(duì)象的 basePath 屬性中,元數(shù)據(jù)設(shè)置完成后,在通過(guò) Reflect.defineMetadata 方法進(jìn)行元數(shù)據(jù)的保存。

下面我們用一張圖來(lái)說(shuō)明一下 @Controller 裝飾器的處理流程:

在 OvernightJS 項(xiàng)目中,所使用的 Reflect API 是來(lái)自 reflect-metadata 這個(gè)第三方庫(kù)。該庫(kù)提供了很多 API 用于操作元數(shù)據(jù),這里我們只簡(jiǎn)單介紹幾個(gè)常用的 API:

 
 
 
 
  1. // define metadata on an object or property
  2. Reflect.defineMetadata(metadataKey, metadataValue, target);
  3. Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
  4. // check for presence of a metadata key on the prototype chain of an object or property
  5. let result = Reflect.hasMetadata(metadataKey, target);
  6. let result = Reflect.hasMetadata(metadataKey, target, propertyKey);
  7. // get metadata value of an own metadata key of an object or property
  8. let result = Reflect.getOwnMetadata(metadataKey, target);
  9. let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);
  10. // get metadata value of a metadata key on the prototype chain of an object or property
  11. let result = Reflect.getMetadata(metadataKey, target);
  12. let result = Reflect.getMetadata(metadataKey, target, propertyKey);
  13. // delete metadata from an object or property
  14. let result = Reflect.deleteMetadata(metadataKey, target);
  15. let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);

相信看到這里,可能有一些小伙伴會(huì)有疑問(wèn),通過(guò) Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用呢?這里我們先記住這個(gè)問(wèn)題,后面我們?cè)賮?lái)分析它,接下來(lái)我們來(lái)開(kāi)始分析 @Get 裝飾器。

2.3 @Get 裝飾器
在前面創(chuàng)建的簡(jiǎn)單 Web 服務(wù)器中,我們通過(guò)以下方式來(lái)使用 @Get 裝飾器,該裝飾器用于配置 Get 請(qǐng)求:

 
 
 
 
  1. export class UserController {
  2.   @Get("")
  3.   private getAll(req: Request, res: Response) {
  4.     return res.status(200).json({
  5.       message: "成功獲取所有用戶(hù)",
  6.     });
  7.   }
  8. }

@Get 裝飾器應(yīng)用在 UserController 類(lèi)的 getAll 方法上,它屬于方法裝飾器。它的定義如下所示:

 
 
 
 
  1. // src/core/lib/decorators/method.ts
  2. export function Get(path?: string | RegExp): MethodDecorator & PropertyDecorator {
  3.   return helperForRoutes(HttpVerb.GET, path);
  4. }

與 Controller 函數(shù)一樣,Get 函數(shù)也是一個(gè)裝飾器工廠,調(diào)用該函數(shù)之后會(huì)返回 MethodDecorator & PropertyDecorator 的交叉類(lèi)型。除了 Get 請(qǐng)求方法之外,常見(jiàn)的 HTTP 請(qǐng)求方法還有 Post、Delete、Put、Patch 和 Head 等。為了統(tǒng)一處理這些請(qǐng)求方法,OvernightJS 內(nèi)部封裝了一個(gè) helperForRoutes 函數(shù),該函數(shù)的具體實(shí)現(xiàn)如下:

 
 
 
 
  1. // src/core/lib/decorators/method.ts
  2. function helperForRoutes(httpVerb: HttpDecorator, path?: string | RegExp): MethodDecorator & PropertyDecorator {
  3.   return (target: Object, propertyKey: string | symbol): void => {
  4.       let newPath: string | RegExp;
  5.       if (path === undefined) {
  6.           newPath = '';
  7.       } else if (path instanceof RegExp) {
  8.           newPath = addForwardSlashToFrontOfRegex(path);
  9.       } else { // assert (path instanceof string)
  10.           newPath = '/' + path;
  11.       }
  12.       addHttpVerbToMethodMetadata(target, propertyKey, httpVerb, newPath);
  13.     };
  14. }

觀察以上代碼可知,在 helperForRoutes 方法內(nèi)部,會(huì)繼續(xù)調(diào)用 addHttpVerbToMethodMetadata 方法把請(qǐng)求方法和請(qǐng)求路徑這些元數(shù)據(jù)保存起來(lái)。

 
 
 
 
  1. // src/core/lib/decorators/method.ts
  2. export function addHttpVerbToMethodMetadata(target: Object, metadataKey: any, 
  3.   httpDecorator: HttpDecorator, path: string | RegExp): void {
  4.     let metadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(metadataKey, target);
  5.     if (!metadata) {
  6.         metadata = {};
  7.     }
  8.     if (!metadata.httpRoutes) {
  9.         metadata.httpRoutes = [];
  10.     }
  11.     const newArr: IHttpRoute[] = [{
  12.       httpDecorator,
  13.       path,
  14.     }];
  15.     newArr.push(...metadata.httpRoutes);
  16.     metadata.httpRoutes = newArr;
  17.     Reflect.defineMetadata(metadataKey, metadata, target);

在 addHttpVerbToMethodMetadata 方法中,會(huì)先獲取已保存的元數(shù)據(jù),如果 metadata 對(duì)象不存在則會(huì)創(chuàng)建一個(gè)空的對(duì)象。然后會(huì)繼續(xù)判斷該對(duì)象上是否含有 httpRoutes 屬性,沒(méi)有的話(huà)會(huì)使用 [] 對(duì)象來(lái)作為該屬性的屬性值。而請(qǐng)求方法和請(qǐng)求路徑這些元數(shù)據(jù)會(huì)以對(duì)象的形式保存到數(shù)組中,最終在通過(guò) Reflect.defineMetadata 方法進(jìn)行元數(shù)據(jù)的保存。

同樣,我們用一張圖來(lái)說(shuō)明一下 @Get 裝飾器的處理流程:

分析完 @Controller 和 @Get 裝飾器,我們已經(jīng)知道元數(shù)據(jù)是如何進(jìn)行保存的。下面我們來(lái)回答 “通過(guò) Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用呢?” 這個(gè)問(wèn)題。

2.4 元數(shù)據(jù)的使用
要搞清楚通過(guò) Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用,我們就需要來(lái)回顧一下前面開(kāi)發(fā)的 SampleServer 服務(wù)器:

 
 
 
 
  1. export class SampleServer extends Server {
  2.   constructor() {
  3.     super(process.env.NODE_ENV === "development");
  4.     this.setupControllers();
  5.   }
  6.   private setupControllers(): void {
  7.     const userController = new UserController();
  8.     super.addControllers([userController]);
  9.   }
  10.   public start(port: number): void {
  11.     this.app.listen(port, () => {
  12.       console.log(`?[server]: Server is running at http://localhost:${PORT}`);
  13.     });
  14.   }
  15. }
  16. const sampleServer = new SampleServer();
  17. sampleServer.start(PORT);

在以上代碼中 SampleServer 類(lèi)繼承于 OvernightJS 內(nèi)置的 Server 類(lèi),對(duì)應(yīng)的 UML 類(lèi)圖如下所示:

此外,在 SampleServer 類(lèi)中我們定義了 setupControllers 和 start 方法,分別用于初始化控制器和啟動(dòng)服務(wù)器。我們?cè)谧远x的控制器上使用了 @Controller 和 @Get 裝飾器,因此接下來(lái)我們的重點(diǎn)就是分析 setupControllers 方法。該方法的內(nèi)部實(shí)現(xiàn)很簡(jiǎn)單,就是手動(dòng)創(chuàng)建控制器實(shí)例,然后調(diào)用父類(lèi)的 addControllers 方法。

下面我們來(lái)分析 addControllers 方法,該方法位于 src/core/lib/Server.ts 文件中,具體實(shí)現(xiàn)如下:

 
 
 
 
  1. // src/core/lib/Server.ts
  2. export class Server {
  3.   public addControllers(
  4.     controllers: Controller | Controller[],
  5.     routerLib?: RouterLib,
  6.     globalMiddleware?: RequestHandler,
  7.   ): void {
  8.        controllers = (controllers instanceof Array) ? controllers : [controllers];
  9.        // ① 支持動(dòng)態(tài)設(shè)置路由庫(kù)
  10.        const routerLibrary: RouterLib = routerLib || Router; 
  11.        controllers.forEach((controller: Controller) => {
  12.          if (controller) {
  13.              // ② 為每個(gè)控制器創(chuàng)建對(duì)應(yīng)的路由對(duì)象
  14.              const routerAndPath: IRouterAndPath | null = this.getRouter(routerLibrary, controller);
  15.              // ③ 注冊(cè)路由
  16.              if (routerAndPath) {
  17.                   if (globalMiddleware) {
  18.                       this.app.use(routerAndPath.basePath, globalMiddleware, routerAndPath.router);
  19.                   } else {
  20.                       this.app.use(routerAndPath.basePath, routerAndPath.router);
  21.                   }
  22.               }
  23.             }
  24.         });
  25.     }
  26. }

addControllers 方法的整個(gè)執(zhí)行過(guò)程還是比較清晰,最核心的部分就是 getRouter 方法。在該方法內(nèi)部就會(huì)處理通過(guò)裝飾器保存的元數(shù)據(jù)。其實(shí) getRouter 方法內(nèi)部還會(huì)處理其他裝飾器保存的元數(shù)據(jù),簡(jiǎn)單起見(jiàn)我們只考慮與 @Controller 和 @Get 裝飾器相關(guān)的處理邏輯。

 
 
 
 
  1. // src/core/lib/Server.ts
  2. export class Server {
  3.  private getRouter(routerLibrary: RouterLib, controller: Controller): IRouterAndPath | null {
  4.         const prototype: any = Object.getPrototypeOf(controller);
  5.         const classMetadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, prototype);
  6.         // 省略部分代碼
  7.         const { basePath, options, ...}: IClassMetadata = classMetadata;
  8.         // ① 基于配置項(xiàng)創(chuàng)建Router對(duì)象
  9.         const router: IRouter = routerLibrary(options);
  10.         // ② 為路由對(duì)象添加路徑和請(qǐng)求處理器
  11.         let members: any = Object.getOwnPropertyNames(controller);
  12.         members = members.concat(Object.getOwnPropertyNames(prototype));
  13.         members.forEach((member: any) => {
  14.             // ③ 獲取方法中保存的元數(shù)據(jù)
  15.             const methodMetadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(member, prototype);
  16.             if (methodMetadata) {
  17.                 const { httpRoutes, ...}: IMethodMetadata = methodMetadata;
  18.                 let callBack: (...args: any[]) => any = (...args: any[]): any => {
  19.                     return controller[member](...args);
  20.                 };
  21.                 // 省略部分代碼
  22.                 if (httpRoutes) { // httpRoutes數(shù)組中包含了請(qǐng)求的方法和路徑
  23.                     // ④ 處理控制器類(lèi)中通過(guò)@Get、@Post、@Put或@Delete裝飾器保存的元數(shù)據(jù)
  24.                     httpRoutes.forEach((route: IHttpRoute) => {
  25.                         const { httpDecorator, path }: IHttpRoute = route;
  26.                         // ⑤ 為router對(duì)象設(shè)置對(duì)應(yīng)的路由信息
  27.                         if (middlewares) {
  28.                             router[httpDecorator](path, middlewares, callBack);
  29.                         } else {
  30.                             router[httpDecorator](path, callBack);
  31.                         }
  32.                     });
  33.                 }
  34.             }
  35.         });
  36.         return { basePath, router, };
  37.     }
  38. }

現(xiàn)在我們已經(jīng)知道 OvernightJS 內(nèi)部如何利用裝飾器來(lái)為控制器類(lèi)配置路由信息,這里阿寶哥用一張圖來(lái)總結(jié) OvernightJS 的工作流程:

在 OvernightJS 內(nèi)部除了 @Controller、@Get、@Post、@Delete 等裝飾器之外,還提供了用于注冊(cè)中間件的 @Middleware 裝飾器及用于設(shè)置異常處理中間件的 @ErrorMiddleware 裝飾器。感興趣的小伙伴可以參考一下阿寶哥的學(xué)習(xí)思路,自行閱讀 OvernightJS 項(xiàng)目的源碼。

希望通過(guò)這篇文章,可以讓小伙伴們對(duì)裝飾器的應(yīng)用場(chǎng)景有一些更深刻的理解。如果你還意猶未盡的話(huà),可以閱讀阿寶哥之前寫(xiě)的 了不起的 IoC 與 DI 這篇文章,該文章介紹了如何利用 TypeScript 裝飾器和 reflect-metadata 這個(gè)庫(kù)提供的 Reflect API 實(shí)現(xiàn)一個(gè) IoC 容器。

三、參考資源

  • Github - overnight
  • expressjs.com

分享名稱(chēng):如何讓你的Express飛起來(lái)
URL鏈接:http://www.5511xx.com/article/dhohejj.html