Exception
- HttpException 과 그 하위 클래스는 기본적으로 그에 해당하는 JSON 응답을 자동 생성한다.
{
"statusCode": 500,
"message": "Internal server error"
}
Exception throw 하기
- contoller 에 하드코딩해서 throw 하는 방법
HttpException
생성자 인자 2개는 각각 response
, statusCode
response
는 string 또는 object
- (optional) 3번째 인자로 object를 추가할 수 있는데, 이는 응답으로 직렬화되지 않고 서버 내부용으로만 사용 (로깅 목적 등)
- response 에는
statusCode
, message
가 포함된 JSON 으로 응답
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
{
"statusCode": 403,
"message": "Forbidden"
}
커스텀 Exception
HttpException
을 상속하여 커스텀 Exception 을 만들어 사용할 수 있음
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
내장된 Exception 목록
@nestjs/common
에 내장된 Exception 목록
BadRequestException
UnauthorizedException
NotFoundException
ForbiddenException
NotAcceptableException
RequestTimeoutException
ConflictException
GoneException
HttpVersionNotSupportedException
PayloadTooLargeException
UnsupportedMediaTypeException
UnprocessableEntityException
InternalServerErrorException
NotImplementedException
ImATeapotException
MethodNotAllowedException
BadGatewayException
ServiceUnavailableException
GatewayTimeoutException
PreconditionFailedException
- 모든 내장된 Exception 은
options
파라미터를 통해 에러 원인과 설명을 추가할 수 있다.
throw new BadRequestException('Something bad happened', { cause: new Error(), description: 'Some error description' })
{
"message": "Something bad happened",
"error": "Some error description",
"statusCode": 400,
}
Exception Filters
- build-in exception 을 사용해도 되지만, 더 세부적으로 정의하고자할 때 Exception filter 사용
- 로깅을 하거나, 응답 객체를 변경하는 등
@Catch
: 어떤 Exception 을 잡을지 설정.
- 인자 없을 경우 모든 Exception 에 대해 적용
catch
메소드에서
host
파라미터를 통해 요청, 응답 접근
exception
을 통해 throw 된 Exception 상태, 메시지 등 접근
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();
const error = exception.getResponse();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
error
});
}
}
- method 레벨, Controller 레벨, global 레벨에서 Exception filter 를 적용할 수 있다
method 레벨 Exception filter 적용
- method 에서는
@UseFilters
데코레이터를 통해 적용할 filter 를 설정한다.@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
- 위에서는 인스턴스화해서 지정했지만, 클래스명만 지정할 경우 인스턴스화는 프레임워크에 맡기고 Dependency Injection 하도록 할 수 있다.
- DI 를 사용하는 쪽이 메모리 사용량도 적고, 인스턴스를 재사용할 수 있어 권장된다.
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
controller 레벨 Exception filter 적용
global 레벨 Exception filter 적용
- Global 로 적용할 경우, 모든 controller 와 모든 route handler 에 적용된다.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
- 위처럼 module 외부에서 등록된 필터는 주입할 수 없는 문제가 있기 때문에 module 에 지정할 수 있다.
- 단, 특정 module에서 지정하긴 하지만, 해당 module에서만 사용하는 것이 아니라 전역으로 적용된다.
- 그렇기 때문에 보통 filter 를 정의하는 module 에 지정하도록 권장하고 있다.
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
custom filter 정의
BaseExceptionFilter
를 상속하여 커스텀 필터를 정의할 수 있다.
- catch 메소드를 정의해야한다.
- 필터는 직접 new 로 인스턴스화하지 않고, 프레임워크에서 자동으로 인스턴스화하도록 한다.
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 필터로 적용할 때에는 아래와 같은 방법을 사용한다.
- 상기한
APP_FILTER
를 이용하는 방법
- HttpAdapter 를 주입하는 방법
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
await app.listen(3000);
}
bootstrap();
references