Nest 미들웨어는 기본적으로 express 미들웨어와 동일하다.
미들웨어 기능은 다음 작업을 수행할 수 있습니다.
모든 코드를 실행합니다.
요청 및 응답 객체를 변경합니다.
요청-응답 주기를 종료합니다.
스택에서 다음 미들웨어 함수를 호출합니다.
현재 미들웨어 기능이 요청-응답 주기를 종료하지 않으면 next()다음 미들웨어 기능으로 제어를 전달하도록 호출해야 합니다. 그렇지 않으면 요청이 중단됩니다.
미들웨어 생성
nest g mi 미들웨어 명
처음 미들웨어를 생성했을때 req,res,next가 any로 되어있으니 직접 수정해주고 미들웨어를 만들어주자.
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private logger = new Logger('HTTP') //nest.js에서는 Logger를 기본적으로 지원해준다.
use(req: Request, res: Response, next: NextFunction) {
this.logger.log(req.ip,req.originalUrl)
next();
}
}
그후 의존성 주입을해야하는데 @module 데코레이터로는 미들웨어 의존성주입을 하지 못한다.
공식문서에 모듈 클래스의 configure() 메소드를 이용하라고 적혀있다.
export class BoardModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}
필터 만들기
Nest에서 에러처리하는건 httpException()임
Filters파일을 만들어서 원하는 형식으로 반환시킬 수 있음
http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException) //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(); //httpException의 stats받기
const err = exception.getResponse(); //httpException의 인자 받기
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
필터를 적용시키는 방법은 각 코드마다 적용시키는 방법과 전역에 적용시키는 방법이 있음
@useFilters(필터명) 을적용시키면된다. 클래스,메소드 둘다 됨
@Get('errorTest')
@UseFilters(HttpExceptionFilter)
errTest(){
throw new HttpException('고의적 에러',401);
}
main.ts에 app.useGlobalFilters(new 필터명)을 사용하면 전역에서 예외 처리가 된다
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
파이프란? 클라이언트 요청에서 들어오는 데이터를 유효성 검사 및 변환을 수행하여 서버가 원하는 데이터를 얻을 수 있도록 도와주는 클래스입니다.
Pipes는 변환과 유효성 체크가 있다.
Controller의 핸들러 레벨에서 @usePipes를 사용해 사용할 수 있다.
@Get(':id')
@UsePipes(pipe)
getBoardById(@Param('id') id: string) {
return this.boardService.getBoardById(id);
}
핸들러보다 더 좁은개념, 핸들러 전체가아니라 파라미터 하나에만 적용
@Get(':id')
findOne(@Param('id',ParseIntPipe) id:number){
console.log(typeof id)
return `${id}`;
}
전역으로 관리하고 싶으면 main.ts에 Pipe를 넣으면된다
app.useGlobalPipes(new ValidationPipe())
모든파이프에선 PipeTransform라는 인터페이스를 구현해야한다.
모든파이프에는 tranform 메소드가 필요하다
tranform 메소드에서 return된 값은 Route Handler로 전달된다.
readonly StatusOptions =[
BoardStatus.PRIVATE,
BoardStatus.PUBLIC
]
transform(value: any, metadata: ArgumentMetadata) {
value=value.toUpperCase();
if(!this.isStatusValid(value)){
throw new BadRequestException(`${value} is not Status`)
}
return value
}
private isStatusValid(status:any){
const index = this.StatusOptions.indexOf(status);
return index !== -1;
}
이 경우 매개변수가 PRIVATE 또는 PUBLIC이 아닐경우 에러처리를 한다.
컨트롤러를 불러오기전에 한번호출, 완료후 한번 더 호출 하는과정이다.
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class UndifinedToNullInterceprtor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
console.log('before...');
//전부분
return next
.handle()
.pipe(map((data) => (data === undefined ? null : data))); //후부분
}
}
마찬가지로 @useInterceptors로 장착하거나 main.ts에서 global로 사용할 수 있다.
- Incoming request
- Globally bound middleware
- Module bound middleware
- Global guards
- Controller guards
- Route guards
- Global interceptors (pre-controller)
- Controller interceptors (pre-controller)
- Route interceptors (pre-controller)
- Global pipes
- Controller pipes
- Route pipes
- Route parameter pipes
- Controller (method handler)
- Service (if exists)
- Route interceptor (post-request)
- Controller interceptor (post-request)
- Global interceptor (post-request)
- Exception filters (route, then controller, then global)
- Server response