# Websocket
## 網關
本文檔中其他地方討論的大多數概念,如依賴注入、裝飾器、異常過濾器、管道、守衛和攔截器,都同樣適用于網關。只要有可能,Nest將抽象實現細節,以便相同的組件可以跨基于 `http` 的平臺、`WebSockets` 和微服務運行。本節將介紹 `WebSockets` 在 `Nest` 中的應用。
在 `Nest` 中,網關只是一個用 `@WebSocketGateway()` 裝飾器注解的類。從技術上講,網關與平臺無關,這使得它們在創建適配器之后就可以與任何 `WebSockets` 庫兼容。有兩個開箱即用的WS平臺:[socket.io](https://github.com/socketio/socket.io)和[ws](https://github.com/websockets/ws)。你可以選擇最適合你需要的。另外,您可以按照本指南構建自己的適配器。

> 網關可以被看作是`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?