Skip to main content

网关

本文档中讨论的大多数概念,如依赖注入、装饰器、异常过滤器、管道、守卫和拦截器,同样适用于网关。 在可能的情况下,Nest抽象出实现细节,以便相同的组件可以在基于HTTP的平台、WebSockets和微服务上运行。 本节介绍了与WebSockets特定的Nest方面。

在Nest中,网关只是一个带有@WebSocketGateway()装饰器的类。 从技术上讲,网关是与平台无关的,这使它们与创建适配器后兼容任何WebSockets库。 默认情况下,支持两个WS平台:socket.iows。 您可以选择最适合您需求的平台。此外,您还可以按照此指南构建自己的适配器。

note

可以将网关视为提供程序;这意味着它们可以通过类构造函数注入依赖项。 此外,其他类(提供程序和控制器)也可以注入网关。

安装

要开始构建基于WebSockets的应用程序,请首先安装所需的软件包:

npm i --save @nestjs/websockets @nestjs/platform-socket.io

概述

通常情况下,每个网关都监听与HTTP服务器相同的端口,除非您的应用程序不是Web应用程序, 或者您手动更改了端口。通过将参数传递给@WebSocketGateway(80)装饰器, 可以修改此默认行为,其中80是所选端口号。您还可以使用以下结构设置网关使用的 命名空间

@WebSocketGateway(80, { namespace: 'events' })
warning

网关在被引用于现有模块的提供程序数组中之前不会实例化。

可以通过将第二个参数传递给@WebSocketGateway()装饰器, 将任何支持的选项传递给socket构造函数, 如下所示:

@WebSocketGateway(81, { transports: ['websocket'] })

现在,网关正在监听,但我们尚未订阅任何传入的消息。 让我们创建一个处理程序,该处理程序将订阅events消息并用相同的数据回应用户。

events.gateway.ts
@SubscribeMessage('events')
handleEvent(@MessageBody() data: string): string {
return data;
}
tip

@SubscribeMessage()@MessageBody()装饰器是从@nestjs/websockets软件包导入的。

创建网关后,我们可以在我们的模块中注册它。

events.module.ts
@Module({
providers: [EventsGateway]
})
export class EventsModule {}

您还可以通过将属性键传递给装饰器,从传入的消息体中提取它:

events.gateway.ts
@SubscribeMessage('events')
handleEvent(@MessageBody('id') id: number): number {
// id === messageBody.id
return id;
}

如果不想使用装饰器,以下代码在功能上是等效的:

events.gateway.ts
@SubscribeMessage('events')
handleEvent(client: Socket, data: string): string {
return data;
}

在上面的例子中,handleEvent()函数接受两个参数。 第一个是特定于平台的socket实例, 而第二个是从客户端接收的数据。 尽管这种方法不推荐,因为它需要在每个单元测试中模拟socket实例。

收到events消息后,处理程序将使用与网络发送的相同数据发送确认。 此外,还可以使用特定于库的方法发出消息,例如使用client.emit()方法。 为了访问已连接的socket实例,请使用@ConnectedSocket()装饰器。

events.gateway.ts
@SubscribeMessage('events')
handleEvent(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
): string {
return data;
}
tip

@ConnectedSocket()装饰器是从@nestjs/websockets软件包导入的。

但是,在这种情况下,您将无法利用拦截器。如果不想响应用户, 可以简单地跳过return语句(或显式返回"虚假"值,例如undefined)。

现在,当客户端发出以下消息时:

socket.emit('events', { name: 'Nest' });

handleEvent()方法将被执行。 为了监听从上述处理程序中发出的消息,客户端必须附加相应的确认监听器:

socket.emit('events', { name: 'Nest' }, (data) => console.log(data));

多重响应

确认仅分发一次。此外,它不受本机WebSockets实现的支持。 为了解决此限制,可以返回一个包含两个属性的对象。 事件是发出事件的名称,数据必须转发到客户端。

events.gateway.ts
@SubscribeMessage('events')
handleEvent(@MessageBody() data: unknown): WsResponse<unknown> {
const event = 'events';
return { event, data };
}
tip

WsResponse接口是从@nestjs/websockets软件包导入的。

warning

如果您的数据字段依赖于ClassSerializerInterceptor, 则应返回实现WsResponse的类实例,因为它会忽略普通的JavaScript对象响应。

为了监听传入的响应,客户端必须应用另一个事件监听器。

socket.on('events', (data) => console.log(data));

异步响应

消息处理程序可以同步或异步地响应。因此,支持异步方法。 消息处理程序还可以返回Observable,在这种情况下,将在流完成之前发出结果值。

events.gateway.ts
@SubscribeMessage('events')
onEvent(@MessageBody() data: unknown): Observable<WsResponse<number>> {
const event = 'events';
const response = [1, 2, 3];

return from(response).pipe(
map(data => ({ event, data })),
);
}

在上面的示例中,消息处理程序将3次响应(数组中的每个项目)。

生命周期挂钩

有3个有用的生命周期挂钩可用。它们都有相应的接口,并在以下表格中描述:

  • OnGatewayInit:强制实现afterInit()方法。以库特定的服务器实例作为参数(如果需要,还会传递其余实例)。
  • OnGatewayConnection:强制实现handleConnection()方法。以库特定的客户端socket实例作为参数。
  • OnGatewayDisconnect:强制实现handleDisconnect()方法。以库特定的客户端socket实例作为参数。
tip

每个生命周期接口都是从@nestjs/websockets软件包中公开的。

服务器

有时,您可能希望直接访问本机、平台特定的服务器实例。 将对此对象的引用作为参数传递给afterInit()方法(OnGatewayInit接口)。 另一个选项是使用@WebSocketServer()装饰器。

@WebSocketServer()
server: Server;
tip

@WebSocketServer()装饰器是从@nestjs/websockets软件包导入的。

Nest将在准备好使用后自动将服务器实例分配给此属性。

例子

此处提供了一个工作示例。