在 NestJS 中抛出与 `class-validator` 相同的错误格式

Posted

技术标签:

【中文标题】在 NestJS 中抛出与 `class-validator` 相同的错误格式【英文标题】:Throw same error format as `class-validator` in NestJS 【发布时间】:2020-06-01 20:36:35 【问题描述】:

让我们在 NestJS 项目中拥有这个控制器:

  @Post('resetpassword')
  @HttpCode(200)
  async requestPasswordReset(
    @Body() body: RequestPasswordResetDTO,
  ): Promise<boolean> 
    try 
      return await this.authService.requestPasswordReset(body);
     catch (e) 
      if (e instanceof EntityNotFoundError) 
        // Throw same exception format as class-validator throwing (ValidationError)
       else throw e;
    
  

Dto 定义:

export class RequestPasswordResetDTO 
  @IsNotEmpty()
  @IsEmail()
  public email!: string;

this.authService.requestPasswordReset(body); 抛出EntityNotFoundError 异常时,我想以ValidationError 格式(属性、值、约束等)抛出错误。

如何手动创建此错误?当class-validator 的 DTO 验证失败时,就会抛出这些错误。这些可以只是静态验证,而不是异步数据库验证。

所以最终的API响应格式应该是例如:


    "statusCode": 400,
    "error": "Bad Request",
    "message": [
        
            "target": 
                "email": "not@existing.email"
            ,
            "value": "not@existing.email",
            "property": "email",
            "children": [],
            "constraints": 
                "exists": "email address does not exists"
            
        
    ]

我需要它有一致的错误处理:)

【问题讨论】:

您可以创建一个异步验证器。请参阅此处的示例:github.com/typestack/… 【参考方案1】:

ValidationPipe 添加到您的应用时,请提供自定义exceptionFactory

  app.useGlobalPipes(
    new ValidationPipe(
      exceptionFactory: (validationErrors: ValidationError[] = []) => 
        return new BadRequestException(validationErrors);
      ,
    )
  );

这应该是您获得预期结果所需的全部内容。

为了比较,你可以查看原始 NestJS 版本here。

【讨论】:

我认为 NestJ 曾经一度总是返回 ValidationErrors,但在最新版本中,它们默认使用自己的异常过滤器。我可能是错的,但我确实有一次遇到了一系列 ValidationErrors。那或者我在某处更改了配置。 你如何访问这些值? 不完全确定你在问什么,@John。在异常工厂箭头函数中,来自class-validator 的原始验证错误可以在validationErrors 变量中访问。【参考方案2】:

您可以使用Exception Filter 来创建针对该异常的自定义响应 首先我们定义异常过滤器:

import  ExceptionFilter, Catch, ArgumentsHost, HttpException  from '@nestjs/common';
import  Request, Response  from 'express';
// import  EntityNotFoundError  from 'wherever';

@Catch(EntityNotFoundError)
export class EntityNotFoundExceptionFilter 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();

    response
      .status(status)
      .json(
        "statusCode": 400,
        "error": "Bad Request",
        "message": [
          
            "target": ,
            "property": "email",
            "children": [],
            "constraints": 
              "isEmail": "email must be an email"
            
          ,
          // other field exceptions
        ]
      );
  

然后回到你的控制器,你使用过滤器:

  // ...
  import  EntityNotFoundExceptionFilter  from 'its module';
  // ...
  @Post('resetpassword')
  @HttpCode(200)
  @UseFilters(EntityNotFoundExceptionFilter)
  async requestPasswordReset(
    @Body() body: RequestPasswordResetDTO
  ): Promise<boolean> 
      return await this.authService.requestPasswordReset(body);
  

这应该可以正常工作。

【讨论】:

EntityNotFoundErrorHttpException 不同,您的示例将在 exception.getStatus() 上失败 这很糟糕。您需要为类验证器中的每条错误消息创建一个自定义异常过滤器。这就像 30 多个异常过滤器。不用了!【参考方案3】:

我们可以取回class-validator抛出的异常响应并设置为响应,

import 
  ArgumentsHost,
  BadRequestException,
  Catch,
  ExceptionFilter
 from '@nestjs/common';

@Catch()
export class ValidationFilter < T > implements ExceptionFilter 
  catch (exception: T, host: ArgumentsHost) 
    if (exception instanceof BadRequestException) 
      const response = host.switchToHttp().getResponse();
      response.status(exception.getStatus())
        .json(exception.getResponse());
    
  

控制器应该看起来,

@Post('create')
@UsePipes(ValidationPipe)
@UseFilters(ValidationFilter)
async create(@Body() body: CreateDto) 

【讨论】:

以上是关于在 NestJS 中抛出与 `class-validator` 相同的错误格式的主要内容,如果未能解决你的问题,请参考以下文章

Nestjs(graphql)中不同类型的异常处理

异常抛出与捕获的思考

反应式设计:抛出与发布错误

关于java异常抛出与返回值

尝试安装类验证器和类转换器时,NestJS“警告 EBADENGINE 不支持的引擎 @angulardevkit”

在构造函数中抛出 ArgumentNullException?