nestjs 错误处理方法是什么(业务逻辑错误 vs. http 错误)?
What is the nestjs error handling approach (business logic error vs. http error)?
在使用 NestJS 创建 API 时,我想知道哪种方法是处理 errors/exception 的最佳方式。
我发现了两种不同的方法:
- 有单独的服务和验证管道
throw new Error()
,有控制器 catch
它们,然后抛出适当类型的 HttpException
(BadRequestException
, ForbiddenException
等..)
- 让控制器简单地调用负责处理那部分业务逻辑的 service/validation 管道方法,并抛出适当的
HttpException
.
两种方法各有利弊:
- 这似乎是正确的方法,但是,服务可以 return
Error
出于不同的原因,我如何从控制器知道哪种 HttpException
到return?
- 非常灵活,但在服务中包含
Http
相关内容似乎是错误的。
我想知道,哪一种(如果有的话)是“nest js”的实现方式?
你是怎么处理这件事的?
您可能不仅希望将服务绑定到 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;
}
}
}
}
然后
假设您的业务逻辑抛出一个 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)
添加到控制器的 class 或方法中来使用它;甚至作为所有路线的全球拦截器。当然你也可以在一个拦截器中映射多个错误
在此 codesandbox 中尝试一下。
Nest Js 提供异常过滤器,处理应用层未处理的错误,所以我修改为return500,非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);
}
}
在使用 NestJS 创建 API 时,我想知道哪种方法是处理 errors/exception 的最佳方式。 我发现了两种不同的方法:
- 有单独的服务和验证管道
throw new Error()
,有控制器catch
它们,然后抛出适当类型的HttpException
(BadRequestException
,ForbiddenException
等..) - 让控制器简单地调用负责处理那部分业务逻辑的 service/validation 管道方法,并抛出适当的
HttpException
.
两种方法各有利弊:
- 这似乎是正确的方法,但是,服务可以 return
Error
出于不同的原因,我如何从控制器知道哪种HttpException
到return? - 非常灵活,但在服务中包含
Http
相关内容似乎是错误的。
我想知道,哪一种(如果有的话)是“nest js”的实现方式?
你是怎么处理这件事的?
您可能不仅希望将服务绑定到 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;
}
}
}
}
然后
假设您的业务逻辑抛出一个 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)
添加到控制器的 class 或方法中来使用它;甚至作为所有路线的全球拦截器。当然你也可以在一个拦截器中映射多个错误
在此 codesandbox 中尝试一下。
Nest Js 提供异常过滤器,处理应用层未处理的错误,所以我修改为return500,非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);
}
}