NestJs 自定義裝飾器

2023-09-08 11:36 更新

Nest 是基于裝飾器這種語言特性而創(chuàng)建的。在很多常見的編程語言中,裝飾器是一個廣為人知的概念,但在 JavaScript 世界中,這個概念仍然相對較新。所以為了更好地理解裝飾器是如何工作的,你應該看看 這篇 文章。下面給出一個簡單的定義:

ES2016 裝飾器是一個表達式,它返回一個可以將目標、名稱和屬性描述符作為參數(shù)的函數(shù)。通過在裝飾器前面添加一個 @ 字符并將其放置在你要裝飾的內(nèi)容的最頂部來應用它??梢詾轭?、方法或?qū)傩远x裝飾器。

參數(shù)裝飾器

Nest 提供了一組非常實用的參數(shù)裝飾器,可以結(jié)合 HTTP 路由處理器(route handlers)一起使用。下面的列表展示了Nest 裝飾器和原生 Express(或 Fastify)中相應對象的映射。

@Request(),@Req()req
@Response(),@Res()res
@Next()next
@Session()req.session
@Param(param?: string)req.params / req.params[param]
@Body(param?: string)req.body / req.body[param]
@Query(param?: string)req.query / req.query[param]
@Headers(param?: string)req.headers / req.headers[param]
@Ip()req.ip
@HostParam()req.hosts

另外,你還可以創(chuàng)建自定義裝飾器。這非常有用。

在 Node.js 中,會經(jīng)常將需要傳遞的值加到請求對象的屬性中。然后在每個路由處理程序中手動提取它們,使用如下代碼:

const user = req.user;

為了使代碼更具可讀性和透明性,我們可以創(chuàng)建一個 @User() 裝飾器并在所有控制器中使用它。

user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
  const request = ctx.switchToHttp().getRequest();
  return request.user;
});

現(xiàn)在你可以在任何你想要的地方很方便地使用它。

@Get()
async findOne(@User() user: UserEntity) {
  console.log(user);
}

傳遞數(shù)據(jù)

當裝飾器的行為取決于某些條件時,可以使用 data 參數(shù)將參數(shù)傳遞給裝飾器的工廠函數(shù)。 一個用例是自定義裝飾器,它通過鍵從請求對象中提取屬性。 例如,假設(shè)我們的身份驗證層驗證請求并將用戶實體附加到請求對象。 經(jīng)過身份驗證的請求的用戶實體可能類似于:

{
  "id": 101,
  "firstName": "Alan",
  "lastName": "Turing",
  "email": "alan@email.com",
  "roles": ["admin"]
}

讓我們定義一個將屬性名作為鍵的裝飾器,如果存在則返回關(guān)聯(lián)的值(如果不存在或者尚未創(chuàng)建 user 對象,則返回 undefined)。

user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator((data: string, ctx: ExecutionContext) => {
  const request = ctx.switchToHttp().getRequest();
  const user = request.user;

  return data ? user && user[data] : user;
});

然后,您可以通過控制器中的 @User() 裝飾器訪問以下特定屬性:

@Get()
async findOne(@User('firstName') firstName: string) {
  console.log(`Hello ${firstName}`);
}

您可以使用具有不同鍵的相同裝飾器來訪問不同的屬性。如果用戶對象復雜,使用此方法可以使請求處理程序編寫更容易、并且可讀性更高。

對于 TypeScript 用戶,請注意這 createParamDecorator() 是通用的。這意味著您可以顯式實施類型安全性,例如 createParamDecorator((data, ctx) => ...)或者,在工廠函數(shù)中指定參數(shù)類型,例如createParamDecorator((data: string, ctx) => ...) 。如果省略這兩個, 參數(shù) data 的類型為 any。

使用管道

Nest 對待自定義的路由參數(shù)裝飾器和自身內(nèi)置的裝飾器(@Body(),@Param() 和 @Query())一樣。這意味著管道也會因為自定義注釋參數(shù)(在本例中為 user 參數(shù))而被執(zhí)行。此外,你還可以直接將管道應用到自定義裝飾器上:

@Get()
async findOne(@User(new ValidationPipe()) user: UserEntity) {
  console.log(user);
}
請注意,validateCustomDecorators 選項必須設(shè)置為 true。默認情況下,ValidationPipe 不驗證使用自定義裝飾器注釋的參數(shù)。

裝飾器聚合

Nest 提供了一種輔助方法來聚合多個裝飾器。例如,假設(shè)您要將與身份驗證相關(guān)的所有裝飾器聚合到一個裝飾器中。這可以通過以下方法實現(xiàn):

import { applyDecorators } from '@nestjs/common';

export function Auth(...roles: Role[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized"' })
  );
}

然后,你可以參照以下方式使用 @Auth() 自定義裝飾器:

@Get('users')
@Auth('admin')
findAllUsers() {}

這具有通過一個聲明應用所有四個裝飾器的效果。

來自 @nestjs/swagger 依賴中的 @ApiHideProperty() 裝飾器無法聚合,因此此裝飾器無法正常使用 applyDecorators 方法。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號