WEBSOCKETS
网关
网关是用[@WebSocketGateway](https://github.com/WebSocketGateway) "@WebSocketGateway")()装饰器注解的类。默认情况下,网关使用 socket.io包,但也提供了与广泛的其他库的兼容性,包括本地web套接字实现(阅读更多)。

?> 提示网关的行为与简单的提供者相同,因此它可以毫不费力地通过构造函数注入依赖关系。另外,网关也可以由其他类(提供者和控制器)注入。
安装
首先,我们需要安装所需的软件包:
$ npm i --save @nestjs/websockets
基本
一般来说,除非你的应用程序不是Web应用程序,或者您已手动更改端口,否则每个网关都会在HTTP服务器 运行时监听相同的端口。我们可以通过将参数传递给[@WebSocketGateway](https://github.com/WebSocketGateway) "@WebSocketGateway")(81)装饰器来改变这种行为,其中81是一个选定的端口号。另外,您可以使用以下构造来设置此网关使用的命名空间:
@WebSocketGateway(81, { namespace: 'events' })
!> 警告只有将网关放入提供程序数组中,网关才会启动。
命名空间不是唯一可用的选项。您可以传递此处提及的任何其他财产。这些属性将在实例化过程中传递给套接字构造函数。好的,网关现在正在监听,但我们目前尚未订阅收到的消息。让我们创建一个处理程序,它将订阅事件消息并使用完全相同的数据响应用户。
events.gateway.ts
@SubscribeMessage('events')
onEvent(client, data: any): WsResponse<any> {
const event = 'events';
return { event, data };
}
?> 提示WsResponse接口和[@SubscribeMessage](https://github.com/SubscribeMessage) "@SubscribeMessage")()装饰器都从@ nestjs / common包中导入。
onEvent()函数有2个参数。第一个是库特定的套接字实例,第二个是从客户端接收的数据。从函数返回的对象必须有2个属性。事件是发出事件的名称以及必须转发给客户端的数据。此外,可以使用特定于库的方法发送消息,例如,通过使用client.emit()方法。但是,在这种情况下,您无法使用拦截器。如果你不想回应用户,只是不要返回任何东西(或明确返回“falsy”值,例如,未定义)。
现在,当客户端以下列方式发出消息时:
socket.emit('events', { name: 'Nest' });
onEvent()方法将被执行。此外,为了侦听从上述处理程序中发出的消息,客户端必须附加相应的侦听器:
socket.on('events', (data) => console.log(data));
异步响应
每个消息处理程序可以是同步的或异步的(异步),因此您可以返回Promise。此外,你可以返回RxJS Observable,这意味着你可以返回多个值(它们将被发射,直到流完成)。
events.gateway.ts
@SubscribeMessage('events')
onEvent(client, data: any: 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()方法。将特定于库的客户端套接字实例作为参数。
OnGatewayDisconnect | 强制执行handleDisconnect()方法。将特定于库的客户端套接字实例作为参数。
?>提示每个生命周期接口都来自@ nestjs / websockets包。
特定库的服务器实例
偶尔,您可能希望直接访问本地特定库的服务器实例。此对象的引用作为参数传递给afterInit()方法(OnGatewayInit接口)。第二种方法是使用[@WebSocketServer](https://github.com/WebSocketServer) "@WebSocketServer")()装饰器。
@WebSocketServer() server;
?>注意[@WebSocketServer](https://github.com/WebSocketServer) "@WebSocketServer")()装饰器是从@ nestjs / websockets包中导入的。
当它准备好使用时,Nest会自动将服务器实例分配给该属性。
这里有一个可用的例子
异常过滤器
websockets的异常处理层 工作原理与prime层完全相同。唯一的区别是不要抛出HttpException,你应该抛出WsException。
throw new WsException('Invalid credentials.');
!>注意WsException类是从@ nestjs / websockets包中导入的。
Nest会处理这个异常并用下列数据发出异常消息:
{
status: 'error',
message: 'Invalid credentials.'
}
异常过滤器
异常过滤器 也是非常类似的,并且工作方式与主过滤器完全相同。
ws-exception.filter.ts
import { Catch, WsExceptionFilter } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
@Catch(WsException)
export class ExceptionFilter implements WsExceptionFilter {
catch(exception: WsException, client) {
client.emit('exception', {
status: 'error',
message: `It's a message from the exception filter`,
});
}
}
!>注意全局设置websockets异常过滤器是不可能的。
管道
websockets管道 和普通管道没有区别。唯一应该注意的是,不要抛出HttpException,而应该使用WsException。
!>提示WsException类在[@socketjs](https://github.com/socketjs) "@socketjs") / websockets包中可用。
看守器
常规看守器和websockets看守器 之间有一个区别。websockets guard将从客户端传递的数据而不是expressjs请求对象作为canActivate()函数的参数。此外,当警卫返回false时,它会抛出WsException(而不是HttpException)。
!>提示WsException类在[@socketjs](https://github.com/socketjs) "@socketjs") / websockets包中可用。
拦截器
常规拦截器和websockets拦截器 之间有一个区别。 Websockets拦截器将从客户端传递的数据而不是expressjs请求对象作为intercept()函数的参数。
适配器
Nest websockets模块基于socket.io,但您可以使用WebSocketAdapter接口来引入自己的库。该界面强制实施下表中描述的几种方法:
|
---|---
create | 将套接字实例连接到指定的端口
bindClientConnect | 绑定客户端连接
bindMessageHandlers | 将传入的消息绑定到适当的消息处理程序
另外,还有两种可选的方法:
|
---|---
createWithNamespace | 将套接字实例附加到指定的端口和名称空间(如果您的库支持空间)
bindClientDisconnect | 绑定客户端断开连接事件
出于演示目的,我们将把ws库与Nest应用程序集成在一起。
ws-adapter.ts
import * as WebSocket from 'ws';
import { WebSocketAdapter } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/filter';
export class WsAdapter implements WebSocketAdapter {
create(port: number) {
return new WebSocket.Server({ port });
}
bindClientConnect(server, callback: (...args: any[]) => void) {
server.on('connection', callback);
}
bindMessageHandlers(client: WebSocket, handlers: MessageMappingProperties[], process: (data) => Observable<any>) {
Observable.fromEvent(client, 'message')
.switchMap((buffer) => this.bindMessageHandler(buffer, handlers, process))
.filter((result) => !!result)
.subscribe((response) => client.send(JSON.stringify(response)));
}
bindMessageHandler(buffer, handlers: MessageMappingProperties[], process: (data) => Observable<any>): Observable<any> {
const data = JSON.parse(buffer.data);
const messageHandler = handlers.find((handler) => handler.message === data.type);
if (!messageHandler) {
return Observable.empty();
}
const { callback } = messageHandler;
return process(callback(data));
}
}
由于WsAdapter类可以使用,我们可以使用useWebSocketAdapter()方法设置适配器:
main.ts
const app = await NestFactory.create(ApplicationModule);
app.useWebSocketAdapter(new WsAdapter());
现在Nest会使用我们的WsAdapter而不是默认的WsAdapter。
