NestJS 中的 WebSocket 架构
NestJS 对 WebSocket 提供了完整的支持,采用了"网关(Gateway)"的概念来组织 WebSocket 相关的代码。Gateway 本质上就是 WebSocket 的控制器(Controller),但它处理的是持久连接而非无状态的 HTTP 请求。
NestJS WebSocket 架构中涉及的几个核心概念:
| 概念 | 说明 | 与 HTTP 对应物的关系 |
|---|---|---|
| Gateway | WebSocket 控制器,处理连接和消息 | 等同于 Controller |
| Adapter | 底层适配器,支持 ws 或 Socket.IO | 无直接对应 |
| Exception Filter | 异常过滤器 | 区别:使用 WsException 而非 HttpException |
| Guard | 守卫 | 与 HTTP Guard 用法一致 |
| Interceptor | 拦截器 | 与 HTTP 拦截器用法一致 |
| Pipe | 管道 | 与 HTTP Pipe 用法一致 |
唯一的区别在于异常处理——WebSocket 中要抛出 WsException 而不是 HttpException,因为错误码和错误处理方式不同(WebSocket 没有 404、500 等 HTTP 状态码)。
创建 NestJS 项目并安装依赖
从一个空白项目开始,便于专注于 WebSocket 本身而不被其他代码干扰:
# 创建新项目
nest new ws-messages
cd ws-messages
# 安装 WebSocket 相关依赖
pnpm install @nestjs/websockets @nestjs/platform-ws
bash
安装完成后,在 main.ts 中使用 WsAdapter:
// src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { WsAdapter } from '@nestjs/platform-ws'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.useWebSocketAdapter(new WsAdapter(app))
await app.listen(3000)
}
bootstrap()
typescript
使用 CLI 生成 Gateway
NestJS CLI 提供了 WebSocket Gateway 的生成模板:
nest g gateway gateway --no-spec
bash
选择 WebSocket 类型后,CLI 会自动生成包含 Module、Service 和 DTO 的完整目录结构。如果选择 event 类型而不是 CRUD 类型,则只生成一个简洁的入口文件,后续自己定义事件处理逻辑。
生成的 Gateway 基础结构:
// src/gateway/gateway.gateway.ts
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
WebSocketServer,
} from '@nestjs/websockets'
@WebSocketGateway(3031) // 指定 WebSocket 服务端口
export class GatewayGateway {
@WebSocketServer()
server
@SubscribeMessage('message')
handleMessage(@MessageBody() payload: any): void {
console.log('received:', payload)
}
}
typescript
关键装饰器说明
@WebSocketGateway(port?, options?):将类标记为 WebSocket Gateway。可以指定独立的端口号(与 HTTP 服务端口分开),以及传递给底层 ws 库的配置选项。options 的类型取决于你使用的是 platform-ws 还是 platform-socket.io。
@SubscribeMessage('eventName'):订阅指定的消息事件,等同于 ws 库的 ws.on('message', ...) 但按事件名做了分发。
@MessageBody():提取消息体中的数据。
@WebSocketServer():注入 WebSocket 服务端实例。
客户端连接与通信
创建一个简单的前端页面来连接 NestJS 的 WebSocket 服务:
// 前端代码(Vue/Vite 项目或纯 HTML)
const client = new WebSocket('ws://localhost:3031')
client.onopen = () => {
console.log('connected to server')
// 发送消息,需要包含 event 名称
client.send(JSON.stringify({
event: 'message', // 必须与 @SubscribeMessage 中的事件名一致
data: 'hello from client'
}))
}
client.onmessage = (event) => {
const message = JSON.parse(event.data)
console.log('收到消息:', message)
}
javascript
注意 NestJS 默认的消息格式要求:发送的消息必须是 JSON 字符串,且包含 event 字段来匹配 @SubscribeMessage 中的事件名。这与原生 ws 库的用法不同——原生 ws 只有一个 message 事件,而 NestJS 做了一层事件路由。
服务端向客户端发送消息
@SubscribeMessage('message')
handleMessage(client: any, payload: any): void {
console.log('payload:', payload)
// 通过 client 实例向客户端发送消息
client.send(JSON.stringify({
event: 'message',
data: 'server response message'
}))
}
typescript
client 参数是当前连接的 WebSocket 实例,调用 client.send() 即可向该客户端发送消息。NestJS 的消息格式约定:返回的数据同样需要包含 event 和 data 字段。
版本兼容性注意
课程录制时 NestJS WebSocket 的大版本号是 v10。如果在实践中遇到 API 行为不一致的情况,首先检查安装的 @nestjs/websockets 和 @nestjs/platform-ws 的版本号是否与课程一致。NestJS 的快速迭代偶尔会带来 Breaking Changes。
↑