如果 NestJS 异常过滤器来自 ValidationPipe,则会弄乱错误数组
NestJS Exception filter messes up error array if it comes from ValidationPipe
所以我使用 ValidationPipe 在 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"
}
}
但是当我从 ValidationPipe 收到 ValidationError 时,它应该像以前一样给我一个数组或消息,但它却给出了这条消息:
// 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
不起作用,因为该字段是私有的并且TypeScript会抛出错误:
Property 'response' is private and only accessible within class 'HttpException'.
你们有谁知道我怎样才能到达消息数组,这样我才能正确地格式化我的错误响应?
编辑:很抱歉 post!
正如 @tobias-s 评论的那样,有一个解决方法解决了问题:
Try exception["response"]["message"]. This bypasses the private restriction
所以我使用 ValidationPipe 在 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"
}
}
但是当我从 ValidationPipe 收到 ValidationError 时,它应该像以前一样给我一个数组或消息,但它却给出了这条消息:
// 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
不起作用,因为该字段是私有的并且TypeScript会抛出错误:Property 'response' is private and only accessible within class 'HttpException'.
你们有谁知道我怎样才能到达消息数组,这样我才能正确地格式化我的错误响应?
编辑:很抱歉 post!
正如 @tobias-s 评论的那样,有一个解决方法解决了问题:
Try exception["response"]["message"]. This bypasses the private restriction