nestjs 错误处理方法是什么(业务逻辑错误 vs. http 错误)?

What is the nestjs error handling approach (business logic error vs. http error)?

在使用 NestJS 创建 API 时,我想知道哪种方法是处理 errors/exception 的最佳方式。 我发现了两种不同的方法:

  1. 有单独的服务和验证管道 throw new Error(),有控制器 catch 它们,然后抛出适当类型的 HttpException(BadRequestException, ForbiddenException等..)
  2. 让控制器简单地调用负责处理那部分业务逻辑的 service/validation 管道方法,并抛出适当的 HttpException.

两种方法各有利弊:

  1. 这似乎是正确的方法,但是,服务可以 return Error 出于不同的原因,我如何从控制器知道哪种 HttpException 到return?
  2. 非常灵活,但在服务中包含 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);
  }
}