关于socket.io将异常信息直接以ack的形式返回给客户端的方法
socket.io支持request-response
机制,原理就是使用一个ackId来保证请求与响应的一一对应
而当发生异常的时候(一般就是参数验证异常),nest默认情况下的验证管道是通过emit接口返回给客户端一个exception
事件,如果客户端使用了ack的逻辑并且进行了等待,这种情况下就无法等到对应的ack数据了
socket.io的协议如网址:https://socket.nodejs.cn/docs/v4/socket-io-protocol/#test-suite
,默认的编码器是socket.io-parser
,通过socket.conn.send
可以发送socket.io编码后的数据给客户端,因此,如果希望将socket.io
验证管道的异常通过ack数据包返回到客户端,那么整体的逻辑就是将异常信息通过socket.io-parser
编码,然后使用socket.conn.send
进行编码后数据的直接发送。
根据https://socket.nodejs.cn/docs/v4/socket-io-protocol/#test-suite
socket.io的协议,那么这里遇到个问题就是如何获取到event数据报的ack的id编号,这里有两个方向:
- 直接从socket提供的api中获取,很遗憾,找了资料找不到对应的接口
- socket.io支持自定义解析器,只需要稍微修改一下解析器解析过程,将ack的id信息添加到请求数据中,这样就可以在异常过滤器中通过
ctx.getData()
获取的id信息,然后进行响应的编码发送操作即可
实现
主要需要修改两个位置:
- 解析器解析过程中将ackId添加到用户请求数据中
这样修改之后,在使用的时候就需要使用// 将ackId添加到请求数据中,这样就可以通过验证管道、异常过滤器获取到对应的ackid,然后进行正常的数据ack响应 if (packet.hasOwnProperty("data")) { const temp = packet.data[1] packet.data[1] = { _ackId: packet.id, data: temp, } }
@MessageBody("data")
的形式进行数据获取 - 异常过滤器进行截取异常数据返回ack数据包
import { ArgumentsHost, Catch } from "@nestjs/common" import { BaseWsExceptionFilter, WsException } from "@nestjs/websockets" import { Socket } from "socket.io" import * as parser from "socket.io-parser" // 异常一般就是验证管道返回的异常,这里先针对这种环境处理一下 @Catch(WsException) export class WsExceptionFilter extends BaseWsExceptionFilter { catch(exception: WsException, host: ArgumentsHost) { const ctx = host.switchToWs() const socket = ctx.getClient<Socket>() const requestData = ctx.getData() if (requestData._ackId != undefined) { const encoder = new parser.Encoder() const temp = encoder.encode({ type: parser.PacketType.ACK, data: [exception.getError()], id: requestData._ackId, nsp: "/", }) socket.conn.send(temp[0]) } else { socket.emit("exception", exception.getError()) } } }