网关
本文档中讨论的大多数概念,如依赖注入、装饰器、异常过滤器、管道、守卫和拦截器,同样适用于网关。在可能的情况下,Nest 抽象了实现细节,以便相同的组件可以在基于 HTTP 的平台、WebSockets 和微服务上运行。本节介绍 Nest 中特定于 WebSockets 的方面。
在 Nest 中,网关只是一个用 @WebSocketGateway() 装饰器注解的类。从技术上讲,网关是与平台无关的,这使得它们一旦创建了适配器,就能与任何 WebSockets 库兼容。开箱即用地支持两个 WS 平台:socket.io 和 ws。您可以选择最适合您需求的平台。此外,您还可以按照此 guide 构建自己的适配器。

info 提示 网关可以被视为 providers;这意味着它们可以通过类构造函数注入依赖。此外,网关也可以被其他类(提供者和控制器)注入。
安装
要开始构建基于 WebSockets 的应用程序,首先安装所需的包:
概述
一般来说,每个网关都监听与 HTTP 服务器 相同的端口,除非您的应用不是 Web 应用程序,或者您手动更改了端口。可以通过向 @WebSocketGateway(80) 装饰器传递参数来修改此默认行为,其中 80 是所选的端口号。您还可以使用以下构造设置网关使用的 namespace:
warning 警告 网关只有在现有模块的 providers 数组中被引用时才会被实例化。
您可以将任何受支持的 option 作为第二个参数传递给 @WebSocketGateway() 装饰器的 socket 构造函数,如下所示:
网关现在正在监听,但我们尚未订阅任何传入消息。让我们创建一个处理程序,它将订阅 events 消息,并用完全相同的数据响应用户。
info 提示
@SubscribeMessage()和@MessageBody()装饰器从@nestjs/websockets包中导入。
创建网关后,我们可以将其注册到模块中。
您还可以向装饰器传递一个属性键,以从传入的消息体中提取它:
如果您更倾向于不使用装饰器,以下代码在功能上是等效的:
在上面的示例中,handleEvent() 函数接受两个参数。第一个是平台特定的 socket instance,第二个是从客户端接收的数据。不过,不建议使用这种方法,因为它需要在每个单元测试中模拟 socket 实例。
一旦收到 events 消息,处理程序就会发送一个确认,其中包含通过网络发送的相同数据。此外,还可以使用特定于库的方法来发送消息,例如使用 client.emit() 方法。要访问已连接的 socket 实例,请使用 @ConnectedSocket() 装饰器。
info 提示
@ConnectedSocket()装饰器从@nestjs/websockets包中导入。
但是,在这种情况下,您将无法利用拦截器。如果您不想响应用户,可以简单地跳过 return 语句(或显式返回一个"假"值,例如 undefined)。
现在,当客户端发出如下消息时:
handleEvent() 方法将被执行。为了监听从上述处理程序内部发出的消息,客户端必须附加一个相应的确认监听器:
虽然从消息处理程序返回值会隐式地发送确认,但高级场景通常需要直接控制确认回调。
@Ack() 参数装饰器允许您将 ack 回调函数直接注入到消息处理程序中。
如果不使用该装饰器,此回调将作为方法的第三个参数传递。
多个响应
确认只发送一次。此外,原生 WebSockets 实现不支持它。为了解决这个限制,您可以返回一个包含两个属性的对象。event 是发出的事件的名称,data 是需要转发给客户端的内容。
info 提示
WsResponse接口从@nestjs/websockets包中导入。
warning 警告 如果您的
data字段依赖于ClassSerializerInterceptor,您应该返回一个实现WsResponse的类实例,因为它会忽略普通的 JavaScript 对象响应。
为了监听传入的响应,客户端必须应用另一个事件监听器。
异步响应
消息处理程序可以同步或异步响应。因此,支持 async 方法。消息处理程序还可以返回一个 Observable,在这种情况下,结果值将被发出,直到流完成。
在上面的示例中,消息处理程序将响应 3 次(使用数组中的每个项)。
生命周期钩子
有 3 个有用的生命周期钩子可用。它们都有对应的接口,并在下表中描述:
info 提示 每个生命周期接口都从
@nestjs/websockets包中暴露。
服务器和命名空间
有时,您可能希望直接访问原生的、平台特定的服务器实例。该对象的引用作为参数传递给 afterInit() 方法(OnGatewayInit 接口)。另一种选择是使用 @WebSocketServer() 装饰器。
此外,您可以使用 namespace 属性检索相应的命名空间,如下所示:
@WebSocketServer() 装饰器通过引用 @WebSocketGateway() 装饰器存储的元数据来注入服务器实例。如果您向 @WebSocketGateway() 装饰器提供命名空间选项,@WebSocketServer() 装饰器将返回 Namespace 实例而不是 Server 实例。
warning 注意
@WebSocketServer()装饰器从@nestjs/websockets包中导入。
Nest 会在服务器实例准备好使用后自动将其分配给此属性。
请求作用域的网关
从 NestJS v12 开始,网关支持 request-scoped 提供者。每个连接的套接字都会为每个请求作用域的依赖创建一个新实例,并且该实例在连接期间一直存在——因此它可以安全地保存每个连接的状态。
使用 REQUEST 令牌注入套接字本身,就像在请求作用域的 HTTP 提供者中注入 HTTP 请求一样:
然后网关像注入任何其他提供者一样注入它:
由于作用域与连接绑定,而不是与单个消息绑定,因此上述 sequence 计数器会在该套接字上收到的每条消息中递增,而第二个客户端会获得自己的独立实例。当套接字断开连接时,Nest 会销毁请求作用域的实例。
warning 注意 与 HTTP 一样,请求作用域的提供者会增加每个连接的实例化开销。除非您确实需要每个连接的状态,否则请使用默认的单例作用域。
示例
可用的工作示例位于 here。

