如何捕获验证错误消息 – Nestjs Gateway Socket.io

huangapple go评论94阅读模式
英文:

How to catch validation error message - Nestjs Gateway Socket.io

问题

我有一个验证管道,用于检查用户发送的JSON数据是否有效。验证器运行良好,但我无法捕获错误并将其发送到客户端。我知道在ValidationPipe构造函数中有一个exeptionFactory属性,但我仍然无法捕获错误,它仍然记录在控制台中。

[Nest] 11820 - 01/07/2023, 11:12:25 PM 错误 [WsExceptionsHandler] 错误的请求异常
BadRequestException: 错误的请求异常

这是一段代码

@SubscribeMessage(Stream.Transactions)
@UseGuards(JwtAuthGuard)
@UsePipes(new ValidationPipe())
handleTransactions(
clien: any,
@MessageBody() data: TransactionObject,
) {
let req = this.streamService.transaction(data)
return { event: Stream.Transactions, data: req }
}

英文:

I have a validation pipe that checks if a user-sent JSON data is valid. Validator is working great, but I can not catch the error and then send it to the client. I know about exeptionFactory property in ValidationPipe constructor, but I still can not catch an error and it still logs in the console.

[Nest] 11820  - 01/07/2023, 11:12:25 PM   ERROR [WsExceptionsHandler] Bad Request Exception
BadRequestException: Bad Request Exception

Here is a code

  @SubscribeMessage(Stream.Transactions)
  @UseGuards(JwtAuthGuard)
  @UsePipes(new ValidationPipe())
  handleTransactions(
    clien: any,
    @MessageBody() data: TransactionObject,
  ) {
    let req = this.streamService.transaction(data)
    return { event: Stream.Transactions, data: req }
  }

答案1

得分: 2

我认为你可以创建一个过滤器来获取错误并返回一些特定的数据。但你可以通过以下几种方式来实现:创建一个 Websocket 异常过滤器 来捕获错误,或者使用你在问题中提到的 exceptionFactory 来生成一个 WsException 并在过滤器中捕获它。

主要问题(如果我没记错的话)是管道不返回 WsException,而是 BadRequestException

因此,要使用异常过滤器,当异常是 Bad Request 时,你可以使用以下代码:

@Catch(BadRequestException)
export class BadRequestExceptionsFilter extends BaseWsExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    // 在这里你有异常,可以检查数据
    const wsException = new WsException(exception.getResponse())
    super.catch(wsException, host);
  }
}

注意,这段代码遵循了文档中的示例。

现在不仅可以获取和阅读异常,还可以创建一个正确的 WsException

要使用它,可以将 @UseFilters(BadRequestExceptionsFilter) 添加到你的网关中。

另一种方法是捕获 WS 和 HTTP 异常,并根据需要进行处理,类似于这个示例。思路是只捕获 HTTP 异常以获取 Bad Request,这样你的期望上下文将始终是 WS

@Catch(WsException, HttpException)
export class WsAndHttpExceptionFilter {
  public catch(exception: HttpException, host: ArgumentsHost) {
    // 在这里你有异常,可以检查数据
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    client.send(JSON.stringify({ /* ... */ }))
  }
}

或者你也可以尝试创建一个 exceptionFactory 来返回 WsException

  • 在装饰器中:
@UsePipes(new ValidationPipe({
    exceptionFactory(validationErrors: ValidationError[] = []) {
        // 这里是错误信息
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
}))

请检查一下项目中的工厂是如何实现的,以及这段代码如何尝试遵循相同的方式来返回 WsException

  • 或者在一个扩展 ValidationPipe 的类中:
@Injectable()
export class WSValidationPipe extends ValidationPipe {
  createExceptionFactory() {
    return (validationErrors: ValidationError[] = []) {
        // 这里是错误信息
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
  }
}

顺便说一下,如果你想返回你想要的数据,也可以使用 @Catch(WsException)(仅捕获这个更明确的异常):

@Catch(WsException)
export class WebsocketExceptionsFilter extends BaseWsExceptionFilter { 
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    const data = ctx.getData();
    client.send(JSON.stringify({
      event: 'error',
      ok: false,
      error: exception.getError(),
      data: data  // 或者你想要添加的任何内容
    }))
  }
}

我现在无法测试所有这些代码,但我已经使用了其中的一些片段,对我有用,希望对你有所帮助。

英文:

I think you can create a filter to get the error and return some specific data. But you can do it in a couple ways: Creating a Websocket exception filter to catch the error or use the exceptionFactory you mention in your question to generate a WsException and catch into the filter.

The main problem (if I'm not wrong) is the pipe does not return a WsException but a BadRequestException.

So to use an exception filter, how the exception is Bad Request, you can use this one:

@Catch(BadRequestException)
export class BadRequestExceptionsFilter extends BaseWsExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    // Here you have the exception and you can check the data
    const wsException = new WsException(exception.getResponse())
    super.catch(wsException, host);
  }
}

Note how this code follows the documentation

And now not only you can get and read the exception but also you can create a properly WsException.

To use this you can add @UseFilters(BadRequestExceptionsFilter) into your gateway.

Another way is to catch WS and HTTP exceptions and handle properly you want, something similar to this example. The idea can be to catch the HTTP exception only to get Bad Request so your desired context will be always WS:

@Catch(WsException, HttpException)
export class WsAndHttpExceptionFilter {
  public catch(exception: HttpException, host: ArgumentsHost) {
    // Here you have the exception and you can check the data
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    client.send(JSON.stringify({ /* ... */ }))
  }
}

Or also you can try to create the exceptionFactory to return the WsException.

  • Into the decorator:
@UsePipes(new ValidationPipe({
    exceptionFactory(validationErrors: ValidationError[] = []) {
        // Here are the errors
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
}))

Check how factories are done into the project and how this code tries to follow the same way but returning WsException.

  • Or in a class extending ValidationPipe:
@Injectable()
export class WSValidationPipe extends ValidationPipe {
  createExceptionFactory() {
    return (validationErrors: ValidationError[] = []) {
        // Here are the errors
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
  }
}

By the way, you can also @Catch(WsException) (and only this exception which is clearer) once are thrown if it is util for you to return the data you want:

@Catch(WsException)
export class WebsocketExceptionsFilter extends BaseWsExceptionFilter { 
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    const data = ctx.getData();
    client.send(JSON.stringify({
      event: 'error',
      ok: false,
      error: exception.getError(),
      data: data  // Or whatever you want to add
    }))
  }
}

I can't test now all this code but I've used some of these snippets and works for me, hope it helps.

huangapple
  • 本文由 发表于 2023年1月8日 03:28:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75043185.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定