# Websocket
## 网关
本文档中其他地方讨论的大多数概念,如依赖注入、装饰器、异常过滤器、管道、守卫和拦截器,都同样适用于网关。只要有可能,Nest将抽象实现细节,以便相同的组件可以跨基于 `http` 的平台、`WebSockets` 和微服务运行。本节将介绍 `WebSockets` 在 `Nest` 中的应用。
在 `Nest` 中,网关只是一个用 `@WebSocketGateway()` 装饰器注解的类。从技术上讲,网关与平台无关,这使得它们在创建适配器之后就可以与任何 `WebSockets` 库兼容。有两个开箱即用的WS平台:[socket.io](https://github.com/socketio/socket.io)和[ws](https://github.com/websockets/ws)。你可以选择最适合你需要的。另外,您可以按照本指南构建自己的适配器。
![](https://docs.nestjs.com/assets/Gateways_1.png)
> 网关可以被看作是`provider`,这意味着它可以毫不费力地通过构造函数注入依赖关系。另外,网关也可以由其他类(提供者和控制器)注入。
### 安装
要开始构建基于WebSockets的应用,首先,我们需要安装所需的软件包:
```bash
$ npm i --save @nestjs/websockets @nestjs/platform-socket.io
```
### 概述
一般来说,除非你的应用程序不是 `Web` 应用程序,或者您已手动更改端口,否则每个网关都会在**HTTP服务器**运行时监听相同的端口。我们可以通过将参数传递给 `@WebSocketGateway(80)` 装饰器来改变这种行为,其中 `80` 是一个选定的端口号。另外,您可以使用以下构造来设置此网关使用的[命名空间](https://socket.io/docs/rooms-and-namespaces/):
```typescript
@WebSocketGateway(80, { namespace: 'events' })
```
> 只有将网关放入当前模块的 `providers` 数组中,网关才会实例化。
你可以在 `@WebSocketGateway()` 装饰器的第二个参数中给socket构造函数传入任何支持的选项,如下所示:
```typescript
@WebSocketGateway(81, { transports: ['websocket'] })
```
现在,网关现在正在监听,但我们目前尚未订阅收到的消息。让我们创建一个处理程序,它将订阅`events`消息并使用完全相同的数据响应用户。
>events.gateway.ts
```typescript
@SubscribeMessage('events')
handleEvent(@MessageBody() data: string): string {
return data;
}
```
> `@SubscribeMessage()` 和 `@MessageBody()` 装饰器是从 `@nestjs/websockets` 包中导入的。
如果你不想使用装饰器,下面的代码在功能上是等价的:
> events.gateway.ts
```typescript
@SubscribeMessage('events')
handleEvent(client: Socket, data: string): string {
return data;
}
```
该 `handleEvent()` 函数有两个参数。第一个是特定于平台的[socket](https://socket.io/docs/server-api/#socket)实例,第二个是从客户端接收的数据。但是不建议使用此方法,因为它需要在每个单元测试中模拟 `socket` 实例。
收到消息后,我们会发送一个确认信息,其中包含某人通过网络发送的相同数据。此外,可以使用特定于库的方法发出消息,例如,通过使用 `client.emit()` 方法。 为了访问连接的 `socket` 实例,请使用 `@ConnectedSocket()` 装饰器。
> events.gateway.ts
```typescript
@SubscribeMessage('events')
handleEvent(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
): string {
return data;
}
```
> `@ConnectedSocket()` 装饰器是从 `@nestjs/websockets` 包中导入的。
但是,在这种情况下,您将无法利用拦截器。如果你不想响应用户,你可以简单地跳过 `return` 语句(或者显式地返回 'falsy' 值,例如 'undefined' )。
现在,当客户端发出的消息如下:
```typescript
socket.emit('events', { name: 'Nest' });
```
将执行 `handleEvent()` `法。此外,为了侦听从上述处理程序中发出的消息,客户端必须附加相应的侦听器:
```typescript
socket.emit('events', { name: 'Nest' }, data => console.log(data));
```
### 多个响应
确认仅发送一次。而且,原生 `WebSockets` 不支持它。要解决这个限制,可以返回一个包含两个属性的对象。发射事件的名称 `event` 和将要转发给客户端的 `data` 。
> events.gateway.ts
```typescript
@SubscribeMessage('events')
handleEvent(@MessageBody() data: unknown): WsResponse<unknown> {
const event = 'events';
return { event, data };
}
```
> `WsResponse` 接口是从 `@nestjs/websockets` 包中导入的。
> **警告**
如果您的数据字段依赖于 `ClassSerializerInterceptor`,您应该返回一个实现 `WsResponse` 的类实例,因为它会忽略纯 JavaScript 对象响应。
为了侦听传入的响应,客户端必须应用另一个事件侦听器。
```typescript
socket.on('events', data => console.log(data));
```
### 异步响应
消息处理程序可以同步或异步响应。因此,也支持异步方法。消息处理程序还能够返回一个 `Observable` 对象,在这种情况下,结果值将被出去,直到流完成。
> events.gateway.ts
```typescript
@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()` 装饰器。
偶尔,您可能希望直接访问原生`特定库`的服务器实例。此对象的引用作为参数传递给`afterInit()`方法(`OnGatewayInit`接口)。另一个选项是使用 `@WebSocketServer()` 装饰器。
```typescript
@WebSocketServer()
server: Server;
```
> `@WebSocketServer()` 装饰器是从 `@nestjs/websockets` 包中导入的。
当它准备好使用时,`Nest` 会自动将服务器实例分配给该属性。
[这里](https://github.com/nestjs/nest/tree/master/sample/02-gateways)有一个可用的例子
- 介绍
- 概述
- 第一步
- 控制器
- 提供者
- 模块
- 中间件
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 自定义装饰器
- 基础知识
- 自定义提供者
- 异步提供者
- 动态模块
- 注入作用域
- 循环依赖
- 模块参考
- 懒加载模块
- 应用上下文
- 生命周期事件
- 跨平台
- 测试
- 技术
- 数据库
- Mongo
- 配置
- 验证
- 缓存
- 序列化
- 版本控制
- 定时任务
- 队列
- 日志
- Cookies
- 事件
- 压缩
- 文件上传
- 流式处理文件
- HTTP模块
- Session(会话)
- MVC
- 性能(Fastify)
- 服务器端事件发送
- 安全
- 认证(Authentication)
- 授权(Authorization)
- 加密和散列
- Helmet
- CORS(跨域请求)
- CSRF保护
- 限速
- GraphQL
- 快速开始
- 解析器(resolvers)
- 变更(Mutations)
- 订阅(Subscriptions)
- 标量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 联合类型
- 枚举(Enums)
- 字段中间件
- 映射类型
- 插件
- 复杂性
- 扩展
- CLI插件
- 生成SDL
- 其他功能
- 联合服务
- 迁移指南
- Websocket
- 网关
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 适配器
- 微服务
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定义传输器
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 独立应用
- Cli
- 概述
- 工作空间
- 库
- 用法
- 脚本
- Openapi
- 介绍
- 类型和参数
- 操作
- 安全
- 映射类型
- 装饰器
- CLI插件
- 其他特性
- 迁移指南
- 秘籍
- CRUD 生成器
- 热重载
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模块
- Swagger
- 健康检查
- CQRS
- 文档
- Prisma
- 静态服务
- Nest Commander
- 问答
- Serverless
- HTTP 适配器
- 全局路由前缀
- 混合应用
- HTTPS 和多服务器
- 请求生命周期
- 常见错误
- 实例
- 迁移指南
- 发现
- 谁在使用Nest?