Nest 공식문서 Exception Filter
Exception Filters
Exception filter는 단순하게 exception 을 nest 전체에서 handling 해주는 된다. exception이 코드내에서 따로 처리되지 않는다면 exception filter로 이동해 응답을 전송한다.
Http exception그 하위클래스를 핸들링 하는 global exception filter가 내장되어 있는데,
{
"statusCode": 500,
"message": "Internal server error"
}
이 형태를 반환하는 filter이다.
global Exception filter는 http-errors library를 부분적으로 지원한다. status code가 있고, message가 있다면 그것을 반환해 준다.
Throw Standard Exception
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
같은 방식으로 exception을 만들어 낸다. HttpStatus는 @nestjs/common 패키지에 있다.
@Get()
async findAll() {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN);
}
{
"status": 403,
"error": "This is a custom message"
}
같은 방식으로 객체를 넘겨줄 수 있는데, 객체를 넘겨주면 완전히 응답이 바뀌고, 그냥 문자열만 입력하면 message 부분만 바뀐다
Custom Exceptions
Exception을 직접 만들 수 있는데, HttpException을 상속받는 방식으로 제작한다.
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
@Get()
async findAll() {
throw new ForbiddenException();
}
같은 방식으로 사용된다.
Built-in HTTP Exception
BadRequestException
UnauthorizedException
NotFoundException
ForbiddenException
NotAcceptableException
RequestTimeoutException
ConflictException
GoneException
HttpVersionNotSupportedException
PayloadTooLargeException
UnsupportedMediaTypeException
UnprocessableEntityException
InternalServerErrorException
NotImplementedException
ImATeapotException
MethodNotAllowedException
BadGatewayException
ServiceUnavailableException
GatewayTimeoutException
PreconditionFailedException
Exception Filters
built-in exception filter는 진짜 많은 것들을 자동으로 처리해 주지만, 상황에따라 다른 처리를 하는 것은 할 수 없다. 예를 들면 특정 exception 발생시 log를 남기는 것 같은 것들이 있다. 이때 exception filter를 사용하면 된다
커스텀 logic 을 이용하기에 ArgumentsHost 를 받고, 거기서 추출하는 req,res를 추출하는 방식으로 적용시킬 수 있다.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
모든 HttpException이 발생했을 때 이 이런 방식으로 처리하는 것이 가능하다.
모든 exception filter는 ExceptionFilter를 implements해야 한다.
@Catch 데코레이터는 특정 Exception만 잡을 수 있도록 하고, ,를 이용한 구분으로 여러 Exception을 잡을 수 있다.
Arguments Host
catch() 메서드를 보면exception 과 host 를 parameter에서 받을 수 있는데, exception은 지금 현재 처리되고 있는 Exception 객체를 의미하고, host Parameter는 ArgumentHost를 가져오는데, ArgumentHost는 utility 객체이다.
Req,Res같은 것을 얻을 수 있다. ArgumentHost
Argument Host는 되게 추상화가 많이 되어 있는데, 그 이유는 websocket, microservice 모두에서 사용할 수 있기 때문이다.
Binding Filters
@Post()
@UseFilters(new HttpExceptionFilter())
//@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
UseFilters를 이용해서 filter를 적용할 수 있다. 당연히 , 로 구분하면 여러개 동시 적용이 가능하다. 객체를 넣을 수도 있지만, class를 넣어서 DI와 책임을 framework로 조금 더 옮기는 방식도 가능하다.
가능하다면 class로 넣는 것이 instance보다 좋은데, memory 사용량에서 차이가 생긴다. instance는 모든 모듈에서 쉽게 공유될 수 있다.
UseFilters는 method scope, controller scope, global scope에서 적용할 수 있다.
//클래스
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
//전역
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
단 useGlobalFilters 메서드는 gateway(websocket)나 hybrid applications(react native)에서 제대로 역할을 못 한다.
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
저렇게 안에서 생성하면, useGlobalFilters라고 하는 어떤 모듈에도 포함되지 않는 곳에서 생성되는데, DI관점에서 좋지 않은 코드가 되기에, 위의 코드처럼 직접 넣을 수 있다.
단 이때는 어떤 모듈에서 등록하든 전부 global filter로 작동한다. provider에 배열로 넣어서 많은 것들을 등록할 수 있다.
Catch Everything
@Catch()만 사용했을 경우 모든 경우에서 사용할 수 있다.
이때 아래 코드처럼 작성한다면 platform 에 관계 없이 어떤 상황에서도 잘 작동한다.
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost): void {
// In certain situations `httpAdapter` might not be available in the
// constructor method, thus we should resolve it here.
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const httpStatus =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const responseBody = {
statusCode: httpStatus,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(ctx.getRequest()),
};
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
}
}
일단 이런식으로 플랫폼 독립적인 것도 가능은 하다...
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
super.catch(exception, host);
}
}
이런 방식으로 기본 global filter를 사용하고, 추가로 조금 수정하는 방식이 가능함.
method-scope, csontroller-scope에 해당하는 BaseExceptionFilter를 extend하는 것은 new로 인스턴스화 하지 말아야 한다.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
await app.listen(3000);
}
bootstrap();
첫 방법으로 만든 플랫폼 독립적인거는 이런 방식으로 처리할 수 있다.
Author And Source
이 문제에 관하여(Nest 공식문서 Exception Filter), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@presentsong/Nest-공식문서-Exception-Filter저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)