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

Bat*_*rka 4 typescript class-validator nestjs

让我们在 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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

Dto定义:

export class RequestPasswordResetDTO {
  @IsNotEmpty()
  @IsEmail()
  public email!: string;
}
Run Code Online (Sandbox Code Playgroud)

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

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

所以最终的 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"
            }
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*hua 8

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

  app.useGlobalPipes(
    new ValidationPipe({
      exceptionFactory: (validationErrors: ValidationError[] = []) => {
        return new BadRequestException(validationErrors);
      },
    })
  );
Run Code Online (Sandbox Code Playgroud)

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

为了进行比较,您可以在此处查看原始 NestJS 版本。

  • 我认为 NestJs 在某一时刻总是返回 ValidationErrors,但在最新版本中,它们默认使用自己的异常过滤器。我可能是错的,但我确实曾经遇到过一系列验证错误。或者我在某处更改了配置。 (2认同)

小智 2

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

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
        ]
      });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这应该可以正常工作。

  • 这很糟糕。您需要为类验证器中的每个错误消息创建自定义异常过滤器。这相当于 30 多个异常过滤器。不,谢谢! (4认同)