NestJs - ArgumentsHost, ExecutionContext

choko's avatar
Jun 29, 2024
NestJs - ArgumentsHost, ExecutionContext
 

ArgumentsHost

export interface ArgumentsHost { getArgs<T extends Array<any> = any[]>(): T; getArgByIndex<T = any>(index: number): T; switchToRpc(): RpcArgumentsHost; switchToHttp(): HttpArgumentsHost; switchToWs(): WsArgumentsHost; getType<TContext extends string = ContextType>(): TContext; }
  • 핸들러에 전달되는 인수를 검사하는 메서드 제공
  • 적절한 context(HTTP, RPC, WebSocket)를 선택하여 param을 사용할 수 있다.
  • HTTP 애플리케이션의 경우, exception filter의 catch()문에서 다음과 같이 사용할 수 있다.
    • @Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: any, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); ... } }
 

Current application context

  • generic guards, filter, interceptors를 사용할 때 메서드가 현재 실행 중인 애플리케이션 유형을 판단할 경우, ArgumentHost의 getType()을 사용할 수 있다.
if (host.getType() === 'http') { // do something that is only important in the context of regular HTTP requests (REST) } else if (host.getType() === 'rpc') { // do something that is only important in the context of Microservice requests } else if (host.getType<GqlContextType>() === 'graphql') { // do something that is only important in the context of GraphQL requests }
 

Host Handler arguments

  • getArgs()로 req, res를 얻어올 수있다.
const [req, res, next] = host.getArgs(); const request = host.getArgByIndex(0); const response = host.getArgByIndex(1);
 
  • switchToHttp, switchToRpc, switchToWs 를 사용하여 애플리케이션 컨텍스트를 전환할 수 있다.
/** * Switch context to RPC. */ switchToRpc(): RpcArgumentsHost; /** * Switch context to HTTP. */ switchToHttp(): HttpArgumentsHost; /** * Switch context to WebSockets. */ switchToWs(): WsArgumentsHost;
 
 
 

ExecutionContext

  • ExecutionContext는 ArgumentHost를 상속받고, 현재 프로세스에 대한 추가 세부 정보를 제공한다.
    • export interface ExecutionContext extends ArgumentsHost { getClass<T = any>(): Type<T>; getHandler(): Function; }
    • class, handler name 반환
    • const methodKey = ctx.getHandler().name; // "create" const className = ctx.getClass().name; // "CatsController"
 
 
 

Reflection and metadata

  • Nest는 Reflector.createDecorator() 로 데코레이터를 생성하고 메서드를 @SetMetadata() 로 매핑하는 기능을 이용해 custom metadata를 route handler에 매핑한다.
 
  • Reflection 사용 예시
    • // roles.decorator.ts import { Reflector } from '@nestjs/core'; export const Roles = Reflector.createDecorator<string[]>(); // roles.guard.ts @Injectable() export class RolesGuard { constructor(private reflector: Reflector) {} } // cats.controller.ts @Post() @Roles(['admin']) // => Roles가 admin인 유저만 create를 허용 async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }
    • reflector.get(decorator, context)
      • contextcontext.getHandler() 를 통해 얻음
 
  • 다음과 같이 Roles를 method 레벨과 컨트롤러 레벨에서 같이 사용하는 경우
    • // cats.controller.ts @Roles(['user']) @Controller('cats') export class CatsController { @Post() @Roles(['admin']) async create1(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Post() async create2(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } } // roles.guard.ts @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles1 = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [ context.getHandler(), context.getClass(), ]); console.log("requiredRoles: ", requiredRoles1) const requiredRoles2 = this.reflector.getAllAndMerge(ROLES_KEY, [context.getHandler(), context.getClass()]); console.log("requiredRoles: ", requiredRoles2) } }
    • reflector.getAllAndOverride(metadataKey, targets)
      • 위 경우, create1() 에서는 requiredRoles이 [’Admin’],
      • create2() 에서는 requiredRoles이 [‘User’]가 된다.
    • reflector.getAllAndMerge(metadataKey, targets)
      • 위 경우, create1()에서는 requiredRoles이 [’Admin’, ‘User’]
      • 위 경우, create2()에서는 requiredRoles이 [‘User’]가 된다.
 
  • Roles()로 할당하지 않고, @SetMetadata(ROLES_KEY, ['admin']) 이런식으로 할당하는것도 가능하다.
    • @Post('test2') @SetMetadata('roles', ['test']) @UseGuards(RolesGuard) async create3(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }
 
Share article

Tom의 TIL 정리방