【问题标题】:What is the nestjs error handling approach (business logic error vs. http error)?什么是nestjs错误处理方式(业务逻辑错误vs.http错误)?
【发布时间】:2018-12-09 07:27:43
【问题描述】:

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

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

这两种方法各有利弊:

  1. 这似乎是正确的方法,但是,服务可以返回 Error 出于不同的原因,我如何从控制器知道哪个是相应的 HttpException 返回?
  2. 非常灵活,但在服务中包含 Http 相关内容似乎是错误的。

我想知道,哪一种(如果有的话)是“nest js”的做法?

你是怎么处理这件事的?

【问题讨论】:

    标签: javascript node.js typescript error-handling nestjs


    【解决方案1】:

    您可能不仅希望将服务绑定到 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;
          }
        }
      }
    }
    

    然后

    【讨论】:

    • 谢谢亚历克斯。您将如何使用您发布的代码?在控制器中?
    • 你们是如何实现这项服务的?
    【解决方案2】:

    假设您的业务逻辑抛出 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) 添加到控制器的类或方法来使用它;甚至作为所有路由的全局拦截器。当然,你也可以在一个拦截器中映射多个错误。

    codesandbox 中尝试一下。

    【讨论】:

    • 注意,代码 sn-p 已经使用了新的nest v6拦截器。对于 v5 示例,请查看代码框。
    【解决方案3】:

    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);
      }
    }
    

    【讨论】:

    • 很好的实现!我认为您也可以使用 NestJS 依赖注入语法,因此您不必声明私有属性“logger”然后实例化它。您可以在构造函数中使用“private readonly logger : Logger”,它会自动实例化。
    • 谢谢,注意到将实施和更新答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-15
    • 2015-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    相关资源
    最近更新 更多