在使用NestJS创建API时,我想知道哪种是处理错误/异常的最佳方法。 我发现了两种不同的方法:
throw new Error()
单个服务和验证管道,让控制器catch
它们,然后抛出适当的HttpException
(BadRequestException
、ForbiddenException
等(。- 让控制器简单地调用负责处理该部分业务逻辑的服务/验证管道方法,并抛出适当的
HttpException
。
这两种方法各有利弊:
- 这似乎是正确的方式,但是,服务可以出于不同的原因返回
Error
,我如何从控制器中知道哪些是要返回的相应HttpException
? - 非常灵活,但是在服务中
Http
相关的东西似乎是错误的。
我想知道,哪一个(如果有的话(是"nest js"的方式?
你是如何处理这件事的?
假设您的业务逻辑抛出了一个EntityNotFoundError
,并且您希望将其映射到NotFoundException
。
为此,您可以创建一个转换错误的Interceptor
:
@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// next.handle() is an Observable of the controller's result value
return next.handle()
.pipe(catchError(error => {
if (error instanceof EntityNotFoundError) {
throw new NotFoundException(error.message);
} else {
throw error;
}
}));
}
}
然后,您可以通过向控制器的类或方法添加@UseInterceptors(NotFoundInterceptor)
来使用它;甚至可以作为所有路由的全局拦截器。当然,您也可以在一个拦截器中映射多个错误。
在此代码沙箱中试用一下。
Nest Js 提供了一个异常过滤器,用于处理应用程序层中未处理的错误,因此我将其修改为为返回 500,对于非 Http 的异常,内部服务器错误。然后将异常记录到服务器,然后您可以知道出了什么问题并修复它。
import 'dotenv/config';
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Logger } from '@nestjs/common';
@Catch()
export class HttpErrorFilter implements ExceptionFilter {
private readonly logger : Logger
constructor(){
this.logger = new Logger
}
catch(exception: Error, host: ArgumentsHost): any {
const ctx = host.switchToHttp();
const request = ctx.getRequest();
const response = ctx.getResponse();
const statusCode = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR
const message = exception instanceof HttpException ? exception.message || exception.message?.error: 'Internal server error'
const devErrorResponse: any = {
statusCode,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
errorName: exception?.name,
message: exception?.message
};
const prodErrorResponse: any = {
statusCode,
message
};
this.logger.log( `request method: ${request.method} request url${request.url}`, JSON.stringify(devErrorResponse));
response.status(statusCode).json( process.env.NODE_ENV === 'development'? devErrorResponse: prodErrorResponse);
}
}
您可能不仅希望将服务绑定到 HTTP 接口,还希望将服务绑定到 GraphQL 或任何其他接口。因此,最好将服务中的业务逻辑级异常转换为控制器中的 Http 级异常(BadRequestException、ForbiddenException(。
以最简单的方式,它可能看起来像
import { BadRequestException, Injectable } from '@nestjs/common';
@Injectable()
export class HttpHelperService {
async transformExceptions(action: Promise<any>): Promise<any> {
try {
return await action;
} catch (error) {
if (error.name === 'QueryFailedError') {
if (/^duplicate key value violates unique constraint/.test(error.message)) {
throw new BadRequestException(error.detail);
} else if (/violates foreign key constraint/.test(error.message)) {
throw new BadRequestException(error.detail);
} else {
throw error;
}
} else {
throw error;
}
}
}
}
然后
您还可以使用工厂或处理程序,当控制器捕获异常(错误或域错误(时将其映射到另一个 HttpException。
@Controller('example')
export class ExampleController {
@Post('make')
async make(@Res() res, @Body() data: dataDTO): Promise<any> {
try {
//process result...
return res.status(HttpStatus.OK).json(result);
} catch (error) {
throw AppErrorHandler.createHttpException(error); //<---here is the error type mapping
};
};
};
如果您尝试发送错误的请求错误,以下代码可能会对您有所帮助:
import { Controller, Post, UploadedFiles, UseInterceptors, Body, Get } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
@Post('/multiple')
@UseInterceptors(FilesInterceptor('files'))
async uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body: any) {
console.log('body :', body);
if (!files || !files.length) {
throw new BadRequestException('files should have at least one object');
}
const req: FileDataReq = {
files,
...body,
};
return req;
}