网关
本文档中讨论的大多数概念,如依赖注入、装饰器、异常过滤器、管道、守卫和拦截器,同样适用于网关。 在可能的情况下,Nest抽象出实现细节,以便相 同的组件可以在基于HTTP的平台、WebSockets和微服务上运行。 本节介绍了与WebSockets特定的Nest方面。
在Nest中,网关只是一个带有@WebSocketGateway()
装饰器的类。
从技术上讲,网关是与平台无关的,这使它们与创建适配器后兼容任何WebSockets库。
默认情况下,支持两个WS平台:socket.io
和ws。
您可以选择最适合您需求的平台。此外,您还可以按照此指南构建自己的适配器。
可以将网关视为提供程序;这意味着它们可以通过类构造函数注入依赖项。 此外,其他类(提供程序和控制器)也可以注入网关。
安装
要开始构建基于WebSockets的应用程序,请首先安装所需的软件包:
npm i --save @nestjs/websockets @nestjs/platform-socket.io
概述
通常情况下,每个网关都监听与HTTP服务器相同的端口,除非您的应用程序不是Web应用程序,
或者您手动更改了端口。通过将参数传递给@WebSocketGateway(80)
装饰器,
可以修改此默认行为,其中80
是所选端口号。您还可 以使用以下结构设置网关使用的
命名空间:
@WebSocketGateway(80, { namespace: 'events' })
网关在被引用于现有模块的提供程序数组中之前不会实例化。
可以通过将第二个参数传递给@WebSocketGateway()
装饰器,
将任何支持的选项传递给socket构造函数,
如下所示:
@WebSocketGateway(81, { transports: ['websocket'] })
现在,网关正在监听,但我们尚未订阅任何传入的消息。
让我们创建一个处理程序,该处理程序将订阅events
消息并用相同的数据回应用户。
@SubscribeMessage('events')
handleEvent(@MessageBody() data: string): string {
return data;
}
@SubscribeMessage()
和@MessageBody()
装饰器是从@nestjs/websockets
软件包导入的。
创建网关后,我们可以在我们的模块中注册它。
@Module({
providers: [EventsGateway]
})
export class EventsModule {}
您还可以通过将属性键传递给装饰器,从传入的消息体中提取它:
@SubscribeMessage('events')
handleEvent(@MessageBody('id') id: number): number {
// id === messageBody.id
return id;
}
如果不想使用装饰器,以下代码在功能上是等效的:
@SubscribeMessage('events')
handleEvent(client: Socket, data: string): string {
return data;
}
在上面的例子中,handleEvent()
函数接受两个参数。
第一个是特定于平台的socket
实例,
而第二个是从客户端接收的数据。
尽管这种方法不推荐,因为它需要在每个单元测试中模拟socket
实例。
收到events
消息后,处理程序将使用与网络发送的相同数据发送确认。
此外,还可以使用特定于库的方法发出消息,例如使用client.emit()
方法。
为了 访问已连接的socket
实例,请使用@ConnectedSocket()
装饰器。
@SubscribeMessage('events')
handleEvent(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
): string {
return data;
}
@ConnectedSocket()
装饰器是从@nestjs/websockets
软件包导入的。
但是,在这种情况下,您将无法利用拦截器。如果不想响应用户,
可以简单地跳过return
语句(或显式返回"虚假"值,例如undefined
)。
现在,当客户端发出以下消息时:
socket.emit('events', { name: 'Nest' });
handleEvent()
方法将被执行。
为了监听从上述处理程序中发出的消息,客户端必须附加相应的确认监听器:
socket.emit('events', { name: 'Nest' }, (data) => console.log(data));
多重响应
确认仅分发一次。此外,它不受本机WebSockets实现的支持。 为了解决此限制,可以返回一个包含两个属性的对象。 事件是发出事件的名称,数据必须转发到客户端。
@SubscribeMessage('events')
handleEvent(@MessageBody() data: unknown): WsResponse<unknown> {
const event = 'events';
return { event, data };
}
WsResponse
接口是从@nestjs/websockets
软件包导入的。
如果您的数据字段依赖于ClassSerializerInterceptor
,
则应返回实现WsResponse
的类实例,因为它会忽略普通的JavaScript对象响应。
为了监听传入的响应,客户端必须应用另一个事件监听器。
socket.on('events', (data) => console.log(data));
异步响应
消息处理程序可以同步或异步地响应。因此,支持异步方法。
消息处理程序还可以返回Observable
,在这种情况下,将在流完成之前发出结果值。
@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实例作为参数。
每个生命周期接口都是从@nestjs/websockets
软件包中公开的。
服务器
有时,您可能希望直接访问本机、平台特定的服务器实例。
将对此对象的引用作为参数传递给afterInit()
方法(OnGatewayInit
接口)。
另一个选项是使用@WebSocketServer()
装饰器。
@WebSocketServer()
server: Server;
@WebSocketServer()
装饰器是从@nestjs/websockets
软件包导入的。
Nest将在准备好使用后自动将服务器实例分配给此属性。
例子
此处提供了一个工作示例。