You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免NestJS中@SubscribeMessage异常引发的过期ACK问题?

问题描述

我有一个Nest服务,其中包含若干@SubscribeMessage函数用于获取数据并返回给客户端。当客户端请求非法时,这些函数可能会抛出异常,但客户端调用socket.emitWithAck会一直挂起,直到socket断开连接,且此时也无法看到服务端抛出的错误。示例代码如下:

try {
  const result = socket.emitWithAck('throw-error', data);
  // do something with result
} catch (e) {
  // show error to user
}

用户永远看不到服务端的错误,只会在socket断开数小时后收到“Error: socket has been disconnected”错误。我希望客户端能看到@SubscribeMessage函数中抛出的错误,有没有比给每个@SubscribeMessage方法都套一个大型try/catch(在catch块中发送类似{ error: e }的内容)更优雅的方式?最好是只需配置一次的方案。

优雅解决方案:全局WebSocket异常过滤器

Nest提供了WebSocket异常过滤器能力,只需配置一次就能全局捕获所有@SubscribeMessage方法抛出的异常,并将错误信息返回给客户端,无需逐个方法添加try/catch。

步骤1:创建WebSocket异常过滤器

自定义过滤器实现WsExceptionFilter接口,统一处理异常并返回格式化的错误响应:

import { Catch, ArgumentsHost, WsExceptionFilter } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Catch()
export class AllWsExceptionsFilter implements WsExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const client = host.switchToWs().getClient<Socket>();
    const data = host.switchToWs().getData();
    // 区分自定义WsException和未知异常,格式化错误信息
    const errorResponse = exception instanceof WsException
      ? { error: exception.getError() }
      : { error: '服务器内部错误', message: (exception as Error).message };
    
    // 通过ack回调返回错误(emitWithAck依赖该机制)
    const callback = client._callbacks[data[0]]?.[0];
    if (callback) {
      callback(errorResponse);
      // 清理回调避免内存泄漏
      delete client._callbacks[data[0]];
    }
  }
}

步骤2:全局注册过滤器

在Nest根模块(如AppModule)中,将过滤器注册为全局WebSocket过滤器:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllWsExceptionsFilter } from './filters/all-ws-exceptions.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: AllWsExceptionsFilter,
    },
  ],
})
export class AppModule {}

步骤3:在业务代码中抛出异常

在@SubscribeMessage方法中直接抛出WsException(或自定义继承它的业务异常),过滤器会自动捕获并返回错误:

import { SubscribeMessage, WebSocketGateway, WsException } from '@nestjs/websockets';

@WebSocketGateway()
export class AppGateway {
  @SubscribeMessage('throw-error')
  handleThrowError(client: any, data: any) {
    if (!data.valid) {
      throw new WsException('请求参数非法');
    }
    return { result: 'success' };
  }
}

客户端接收错误

此时客户端的emitWithAck会正常捕获到错误:

try {
  const result = await socket.emitWithAck('throw-error', { valid: false });
  // 处理正常结果
} catch (e) {
  // 这里会收到服务端返回的错误:{ error: '请求参数非法' }
  console.error('请求失败:', e);
}

关键说明

  • 过滤器通过ArgumentsHost获取WebSocket客户端、请求数据和ack回调,确保错误通过ack机制返回,避免请求挂起。
  • 全局注册后,所有@SubscribeMessage方法的异常都会被统一处理,无需逐个添加try/catch。
  • 可根据业务需求扩展过滤器,比如添加错误日志、区分不同异常类型返回不同格式响应。

内容的提问来源于stack exchange,提问作者Jan Thürmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 19:33:14