NestJS - WebsocketGateway 中的 ValidationPipe 返回内部服务器错误
Posted
技术标签:
【中文标题】NestJS - WebsocketGateway 中的 ValidationPipe 返回内部服务器错误【英文标题】:NestJS - ValidationPipe in WebsocketGateway returns Internal Server Error 【发布时间】:2020-06-30 02:43:16 【问题描述】:我正在尝试在 NestJS 中向我的 WebSocketGateway 添加一些验证。代码如下:
// MessageDTO
import IsNotEmpty, MinLength from 'class-validator';
export class MessageDTO
@IsNotEmpty()
username: string;
@IsNotEmpty()
@MinLength(10)
text: string;
// Gateway
import ValidationPipe, UsePipes from '@nestjs/common';
import MessageBody, SubscribeMessage, WebSocketGateway, WsResponse from '@nestjs/websockets';
import MessageService from './message/message.service';
import MessageDTO from './message/message.dto';
import Message from './message/message.entity';
@WebSocketGateway()
export class AppGateway
constructor(private readonly messageService: MessageService)
@UsePipes(new ValidationPipe())
@SubscribeMessage('message')
async handleMessage(@MessageBody() dto: MessageDTO): Promise<WsResponse<Message>>
const message = await this.messageService.saveMessage(dto);
return event: 'message', data: message ;
现在,当我尝试发送不符合验证规则的消息时,它会出错,但客户端始终会收到 status: 'error', message: 'Internal server error'
。此外,Nest 将错误记录到控制台(我认为这不应该发生......?):
thing_api | Error: Bad Request Exception
thing_api | at ValidationPipe.exceptionFactory (/usr/src/app/node_modules/@nestjs/common/pipes/validation.pipe.js:78:20)
thing_api | at ValidationPipe.transform (/usr/src/app/node_modules/@nestjs/common/pipes/validation.pipe.js:50:24)
thing_api | at processTicksAndRejections (internal/process/task_queues.js:89:5)
thing_api | at async resolveParamValue (/usr/src/app/node_modules/@nestjs/websockets/context/ws-context-creator.js:104:31)
thing_api | at async Promise.all (index 0)
thing_api | at async pipesFn (/usr/src/app/node_modules/@nestjs/websockets/context/ws-context-creator.js:106:13)
thing_api | at async /usr/src/app/node_modules/@nestjs/websockets/context/ws-context-creator.js:41:17
thing_api | at async AppGateway.<anonymous> (/usr/src/app/node_modules/@nestjs/websockets/context/ws-proxy.js:11:32)
thing_api | at async WebSocketsController.pickResult (/usr/src/app/node_modules/@nestjs/websockets/web-sockets-controller.js:85:24)
但是,如果我在常规控制器中使用相同的 DTO 和验证管道,它就像一个魅力 - 使用格式错误的有效负载我会收到格式正确的错误消息。有人能指出我做错了什么吗?
【问题讨论】:
您找到解决问题的方法了吗?我也一样 【参考方案1】:BadRequestException
是HttpException
的子类。 Nest 的默认 exception handler for websockets 检查捕获的异常是否为 instanceof WsException
,如果不是则返回未知异常。
要解决这个问题,您可以实现一个过滤器,捕获 BadRequestException
并将其转换为适当的 WsException
,然后让 Nest 的异常过滤器从那里处理异常。
@Catch(BadRequestException)
export class BadRequestTransformationFilter extends BaseWsExceptionFilter
catch(exception: BadRequestException, host: ArgumentHost)
const properException = new WsException(exception.getResponse());
super.catch(properException, host);
【讨论】:
【参考方案2】:我已经创建了我的 SocketValidation 管道
import PipeTransform, Injectable, ArgumentMetadata, ValidationPipe from '@nestjs/common';
import validate from 'class-validator';
import plainToClass from 'class-transformer';
import WsException from '@nestjs/websockets';
@Injectable()
export class SocketValidationPipe implements PipeTransform<any>
constructor()
// super(options)
async transform(value: any, metatype : ArgumentMetadata)
if (!metatype || !this.toValidate(metatype))
return value;
const object = plainToClass(metatype, JSON.parse(value));
const errors = await validate(object);
if (errors.length > 0)
throw new WsException('Wrong message!');//new BadRequestException('Validation failed');
return value;
private toValidate(metatype: Function): boolean
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
【讨论】:
请解释一下这段代码是如何解决这个问题的?【参考方案3】:你可以重写the default websocket filter,捕获http异常和websocket异常。
import ArgumentsHost, Catch, HttpException from '@nestjs/common';
import WsException from '@nestjs/websockets';
import Socket from 'socket.io';
@Catch(WsException, HttpException)
export class WsExceptionFilter
public catch(exception: HttpException, host: ArgumentsHost)
const client = host.switchToWs().getClient();
this.handleError(client, exception);
public handleError(client: Socket, exception: HttpException | WsException)
if (exception instanceof HttpException)
// handle http exception
else
// handle websocket exception
然后在你的网关中使用它
@UseFilters(WsExceptionFilter)
@WebSocketGateway()
export class WorkspacesGateway
【讨论】:
【参考方案4】:ValidationPipe 获得了exceptionFactory 选项,因此您可以将自定义工厂传递给它,例如:
@UsePipes(new ValidationPipe(
exceptionFactory(validationErrors = [])
if (this.isDetailedOutputDisabled)
return new WsException('Bad request');
const errors = this.flattenValidationErrors(validationErrors);
return new WsException(errors);
))
或者扩展内置的ValidationPipe
import Injectable, ValidationPipe from '@nestjs/common';
import WsException from "@nestjs/websockets";
@Injectable()
export class WSValidationPipe extends ValidationPipe
createExceptionFactory()
return (validationErrors = []) =>
if (this.isDetailedOutputDisabled)
return new WsException('Bad request');
const errors = this.flattenValidationErrors(validationErrors);
return new WsException(errors);
;
【讨论】:
以上是关于NestJS - WebsocketGateway 中的 ValidationPipe 返回内部服务器错误的主要内容,如果未能解决你的问题,请参考以下文章
Chrome v88+ 和 Nestjs 服务器上的 socket-io 连接出现 CORS 错误
NestJs套接字身份验证,在handleConnection中发出事件