Nest 是基于裝飾器這種語言特性而創(chuàng)建的。在很多常見的編程語言中,裝飾器是一個廣為人知的概念,但在 JavaScript 世界中,這個概念仍然相對較新。所以為了更好地理解裝飾器是如何工作的,你應該看看 這篇 文章。下面給出一個簡單的定義:
ES2016 裝飾器是一個表達式,它返回一個可以將目標、名稱和屬性描述符作為參數(shù)的函數(shù)。通過在裝飾器前面添加一個 @ 字符并將其放置在你要裝飾的內(nèi)容的最頂部來應用它??梢詾轭?、方法或?qū)傩远x裝飾器。
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);
}
當裝飾器的行為取決于某些條件時,可以使用 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 方法。
更多建議: