NESTJS异常滤镜弄乱错误阵列,如果来自验证pipe

发布于 2025-01-27 22:29:21 字数 3121 浏览 2 评论 0原文

因此,我使用验证管来验证我在Nestjs中的DTO,因此:

// auth.dto.ts
export class AuthDto {
  @IsEmail()
  @IsNotEmpty()
  email: string;
}

没有例外过滤器,错误消息按预期工作。我将电子邮件字段留为空,然后收到一系列错误消息:

// Response - Message array, but no wrapper
{
  "statusCode": 400,
  "message": [
    "email should not be empty",
    "email must be an email"
  ],
  "error": "Bad Request"
}

完美。现在,我想为错误消息实现包装器,因此我创建一个新的过滤器并将其添加到Bootstrap:

// main.ts
async function bootstrap() {
  // ...
  app.useGlobalFilters(new GlobalExceptionFilter());
}
bootstrap();
// global-exception.filter.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Response } from 'express';
import { IncomingMessage } from 'http';

export const getStatusCode = <T>(exception: T): number => {
  return exception instanceof HttpException
    ? exception.getStatus()
    : HttpStatus.INTERNAL_SERVER_ERROR;
};

export const getErrorMessage = <T>(exception: T): string => {
  return exception instanceof HttpException
    ? exception.message
    : String(exception);
};

@Catch()
export class GlobalExceptionFilter<T> implements ExceptionFilter {
  catch(exception: T, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<IncomingMessage>();
    const statusCode = getStatusCode<T>(exception);
    const message = getErrorMessage<T>(exception);

    response.status(statusCode).json({
      error: {
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode,
        message,
      },
    });
  }
}

它对我的大多数错误都非常有用:

// Response - Good format (wrapped), single message expected
{
  "error": {
    "timestamp": "2022-05-11T19:54:59.093Z",
    "path": "/auth/signup",
    "statusCode": 400,
    "message": "Email already in use"
  }
}

但是当我从验证中获得验证时,它应该给我一个数组或一个数组或邮件如上所述,但它给出了此消息:

// Response - Wrapper: check, single message instead of array
{
  "error": {
    "timestamp": "2022-05-11T19:59:17.282Z",
    "path": "/auth/signup",
    "statusCode": 400,
    "message": "Bad Request Exception" // it should be "message": ["not empty", "must be email"]
  }
}

我的异常过滤器中的异常对象具有一个响应字段,其中包含消息数组:

// HttpException object inside the filter class
{
  response: {
    statusCode: 400,
    message: [ 'email should not be empty', 'email must be an email' ],
    error: 'Bad Request'
  },
  status: 400
}

但是exception.Response.message不起作用,因为该字段为私人和打字稿引发错误:

属性'响应'是私人的,只有在“ httpexception”类中访问。

你们中的任何一个知道我如何达到消息阵列,以便我可以格式化我的我错误响应正确?

编辑:对不起,很长的帖子!

So I use the ValidationPipe to validate my DTOs in NestJS, like this:

// auth.dto.ts
export class AuthDto {
  @IsEmail()
  @IsNotEmpty()
  email: string;
}

Without the Exception filter the error message works as intended. I leave the email field empty and I receive an array of error messages:

// Response - Message array, but no wrapper
{
  "statusCode": 400,
  "message": [
    "email should not be empty",
    "email must be an email"
  ],
  "error": "Bad Request"
}

Perfect. Now I want to implement a wrapper for the error messages, so I create a new filter and add it to to bootstrap:

// main.ts
async function bootstrap() {
  // ...
  app.useGlobalFilters(new GlobalExceptionFilter());
}
bootstrap();
// global-exception.filter.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Response } from 'express';
import { IncomingMessage } from 'http';

export const getStatusCode = <T>(exception: T): number => {
  return exception instanceof HttpException
    ? exception.getStatus()
    : HttpStatus.INTERNAL_SERVER_ERROR;
};

export const getErrorMessage = <T>(exception: T): string => {
  return exception instanceof HttpException
    ? exception.message
    : String(exception);
};

@Catch()
export class GlobalExceptionFilter<T> implements ExceptionFilter {
  catch(exception: T, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<IncomingMessage>();
    const statusCode = getStatusCode<T>(exception);
    const message = getErrorMessage<T>(exception);

    response.status(statusCode).json({
      error: {
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode,
        message,
      },
    });
  }
}

It works great for most of my errors:

// Response - Good format (wrapped), single message expected
{
  "error": {
    "timestamp": "2022-05-11T19:54:59.093Z",
    "path": "/auth/signup",
    "statusCode": 400,
    "message": "Email already in use"
  }
}

But when I get a ValidationError from the ValidationPipe it should give me an array or messages like before, but it gives this message instead:

// Response - Wrapper: check, single message instead of array
{
  "error": {
    "timestamp": "2022-05-11T19:59:17.282Z",
    "path": "/auth/signup",
    "statusCode": 400,
    "message": "Bad Request Exception" // it should be "message": ["not empty", "must be email"]
  }
}

The exception object in my exception filter has a response field which contains the message array:

// HttpException object inside the filter class
{
  response: {
    statusCode: 400,
    message: [ 'email should not be empty', 'email must be an email' ],
    error: 'Bad Request'
  },
  status: 400
}

But exception.response.message doesn't work, because the field is private and TypeScript throws an error:
Property 'response' is private and only accessible within class 'HttpException'.

Does any of you know how could I reach the message array, so I could format my error response properly?

EDIT: Sorry for the long post!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

转身泪倾城 2025-02-03 22:29:21

AS @tobias-s 评论说,有一个解决方案,解决了问题:

尝试异常[“响应”] [“消息”]。这绕过了私人限制

As @tobias-s commented, there's a workaround, which solved the problem:

Try exception["response"]["message"]. This bypasses the private restriction

莳間冲淡了誓言ζ 2025-02-03 22:29:21

当您使用@catch装饰器时,您是否可以获得httpexception not,因此我们需要对其进行评估。

让我们创建一个接口“ parse” nest.js内置httpexception类响应:

export interface HttpExceptionResponse {
    statusCode: number;
    message: any;
    error: string;
}

现在我们可以对其进行处理:

export const getErrorMessage = <T>(exception: T): any => {

  if(exception instanceof HttpException) {

    const errorResponse = exception.getResponse();
    const errorMessage = (errorResponse as HttpExceptionResponse).message || exception.message;

    return errorMessage;
  } else {
    return String(exception);
  }
};

except.getResponse()可以是字符串或对象,那是因为我们将其处理为消息:当然,任何

As you are using @Catch decorator, you could get a HttpException or not, so we need to evaluate it.

Let's create an interface to "parse" Nest.js built-in HttpException class response:

export interface HttpExceptionResponse {
    statusCode: number;
    message: any;
    error: string;
}

Now we can process it:

export const getErrorMessage = <T>(exception: T): any => {

  if(exception instanceof HttpException) {

    const errorResponse = exception.getResponse();
    const errorMessage = (errorResponse as HttpExceptionResponse).message || exception.message;

    return errorMessage;
  } else {
    return String(exception);
  }
};

exception.getResponse() can be a string or an object, that's because we handle it as message: any of course.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文