[元件] Custom Decorator

自訂裝飾器本身會在 class 定義時執行,主要用來設定 metadata 或建立參數解析邏輯。而這些結果會在請求流程中,例如 guard、pipe 被使用。
Metadata 裝飾器
透過 CLI 產生的 decorator 預設是 metadata 裝飾器:
import { SetMetadata } from '@nestjs/common';
export const TestMetadata = (...args: string[]) => SetMetadata('test', args);一般是掛在 handler,或是掛在 controller 來套用到全部的路由上:
@Get()@UseGuards(TestAuthGuard)@TestMetadata('test', 'test2')test() { return 'test';}@Controller('test')@TestMetadata('test', 'test2')metadata 在能注入 Reflector 且擁有 ExecutionContext 的元件都能使用,一般多用在 guard 或 interceptor。
在建構函式中注入 Reflector 後就可以在 context.getHandler 取得 metadata:
@Injectable()export class TestAuthGuard implements CanActivate { constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> { const handler = context.getHandler(); const handlerMeta = this.reflector.get<string[]>('test', handler); console.log('handlerMeta', handlerMeta);
return true; }}如果是掛在 controller 上,要改由 context.getClass 來取得 metadata。
Param 裝飾器
createParamDecorator 的參數是一個工廠函式,由我們來決定怎麼處理工廠函式的參數,data: string 代表要傳入裝飾器的資料類型:
export const TestParams = createParamDecorator((data: string, context: ExecutionContext) => {});下面示範從請求物件取出資料,並與 data 結合:
export const TestParams = createParamDecorator((data: string, context: ExecutionContext) => { const ctx = context.switchToHttp(); const request = ctx.getRequest<Request>(); const id = request.params.id; const name = data || 'default name';
return { id, name };});return 的內容 { id, name } 會在 handler 中透過被 @TestParams 修飾的參數來接收,就像 @Query 或 @Param 的用法:
@Controller('test')export class TestController { @Get('/:id') testId(@TestParams('窩愛尼') data: { id: string; name: string }) { const message = `test params: ${data.id} and name: ${data.name}`;
return message; }}所以 data 的型別要與剛剛在 createParamDecorator 在工廠函式定義的 return { id, name } 相同。
假設請求的網址是 /test/123,message 會是 'test params: 123 and name: 窩愛尼'。
裝飾器組合
常用的裝飾器可以透過 applyDecorators 組合,讓程式碼更乾淨。
組合後的裝飾器所接收的參數,可以在工廠函式的參數 ...args:
import { applyDecorators, UseGuards } from '@nestjs/common';import { TestAuthGuard } from 'src/test-auth/test-auth.guard';import { TestMetadata } from './test.decorator';
export const TestComposition = (...args: string[]) => applyDecorators(UseGuards(TestAuthGuard), TestMetadata(...args));@Get()@TestComposition('Here is metadata from TestComposition')test() { return 'test';}傳入的 metadata 'Here is metadata from TestComposition' 透過 TestMetadata 可以在 Reflector 查看。
小結
由於裝飾器本身能夠取得 ExecutionContext,所以通常會搭配 guard 或 interceptor 在請求進入主要邏輯之前協同處理。