【问题标题】:NestJS - ValidationPipe in WebsocketGateway returns Internal Server ErrorNestJS - WebsocketGateway 中的 ValidationPipe 返回内部服务器错误
【发布时间】: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 和验证管道,它就像一个魅力 - 使用格式错误的有效负载我会收到格式正确的错误消息。有人能指出我做错了什么吗?

【问题讨论】:

  • 您找到解决问题的方法了吗?我也一样

标签: typescript websocket nestjs


【解决方案1】:

BadRequestExceptionHttpException 的子类。 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);
          };
        }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-12
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多