带你入门——如何在nestjs中体验websocket
websocket作为一种h5新增的通信方式,在日常开发中很常见,聊天室,数据大屏等项目中都有它的影子,本文将会根据文档以及自己的理解引导大家学习如何在nestjs中实现websocket,并且会搭配vue3做个小demo帮助大家更好的理解,本文内容有点多,大概需要5-8分钟的学习,希望想学习的小伙伴能够耐心看完,知识点不难,希望能够帮助大家快速入门
前言准备
在nestjs官方文档中提到,在nestjs中有两个现成支持的 WS 平台:socket.io 和 ws,这俩都是基于websocket封装的框架,其中socket.io在websocket的基础上集成了强大的功能
-
实时双向通信:Socket.IO 提供了实时的双向通信,允许服务器主动向客户端发送消息,并且客户端也可以向服务器发送消息。这种双向通信方式非常适合实时应用程序的需求,例如聊天或协作工具。
-
自动重新连接:当客户端与服务器之间的连接中断时,Socket.IO 具有自动重新连接的能力,它会尝试重新建立连接,并维护连接的稳定性。
-
分房间/分组:Socket.IO 允许将客户端连接划分到不同的房间或分组中,可以根据不同的需求将客户端进行分组管理,并实现群发或与特定组进行通信。
-
自定义事件:Socket.IO 允许开发者定义和触发自定义事件,这些事件可以是应用程序内部的逻辑事件或用户自定义的事件。通过自定义事件,可以实现灵活的消息传递和通信模式。
-
跨平台支持:Socket.IO 不仅支持浏览器端,还支持服务器端的多种编程语言,如 Node.js、Python、Java 和 .NET 等。这使得开发者能够在不同的环境中使用相同的通信协议和模式。
并且当 WebSocket 连接不可用时,Socket.IO 会使用 HTTP 协议作为备选方案来保持实时通信。
这俩框架选谁都可以,我这里用socket.io来为大家做演示了。
构建准备
先安装对应的包
pnpm i --save @nestjs/websockets @nestjs/platform-socket.io
接着通过创建一个gateway模块,与此同时创建一个event.gateway.ts,用于创建我们的websocket服务
接着在我们的event.gateway.ts中创建我们的类
import {WebSocketGateway} from '@nestjs/websockets'
@WebSocketGateway()
export class EventGateway {
}
@WebSocketGateway是一个装饰器,用于创建WebSocket网关类。WebSocket网关类是用于处理 WebSocket连接和消息的核心组件之一。它充当WebSocket服务端的中间人,负责处理客户端发起的连接请求,并定义处理不同类型消息的逻辑
对于它可以接受一些参数,这里我们就快速过一遍挑重要的说 @WebSocketGateway(80, options)
第一个参数可以传递一个端口号,如果不传默认和http监听的端口一样,也就是main.ts中的端口,nestjs默认是3000,第二个是一些配置项,有很多例如跨域配置cors,以及namespace命名空间,这里只说配置cors跨域,其他的配置项,可以自行去[服务器选项 |Socket.IO]看看
在现在的新版本中客户端连接nestjs端的websocket服务时会出现跨域问题的,所以需要配置一下
@WebSocketGateway({
cors: {
origin: '*',
},
})
配置完这些,我们还需要在module中提供一下,因为网关可以被视为provider,可以注入依赖项,也可以被其他类注入
import { Module } from '@nestjs/common';
import { EventGateway } from './enent.gateway';
@Module({
providers: [EventGateway]
})
export class GatewayModule {}
实现订阅消息
到这里我们就配置好了,但是我们想想一个最简易的聊天是不是得有发送消息和接收消息吧,就是发布订阅模式,nestjs中为我们内置好了对应的装饰器 @SubscribeMessage,看到这个英文单词就知道啥意思了,就是订阅消息的意思,在我们订阅到消息时可以写一下逻辑处理
import { MessageBody, SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'
@WebSocketGateway({ cors: { origin: '*' } })
export class EventGateway {
@SubscribeMessage('newMessage')
handleMessage(@MessageBody() body: any) {
console.log(body);
}
}
@MessageBody这个装饰器可以获取消息,到这里我们的订阅服务已经起来了,打开控制台启动一下项目看看
,看到里面有Websocketscontroller就代表启动成功了
如果不想用装饰器可以这样做,效果一样
@SubscribeMessage('events')
handleEvent(client: Socket, data: any): any {
return data;
}
第一个参数是socket实例,第二个data是从客户端接受的消息,但是官方不推荐这种写法哈,建议还是用装饰器
实现发布消息
那么处理好了订阅消息,如何实现发布消息呢,官方文档中介绍了一个装饰器 @ConnectedSocket,可以实例化一个socket,我们可以通过上面的emit方法来监听一个自定义事件来发布消息
import { ConnectedSocket, MessageBody, SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'
import { Socket } from 'socket.io'
@WebSocketGateway({ cors: { origin: '*' } })
export class EventGateway {
@SubscribeMessage('newMessage')
handleMessage(@MessageBody() body: any, @ConnectedSocket() client: Socket,) {
client.emit('onMessage')
console.log(body);
}
}
接着打开postman来测试,我推荐这款工具的原因是,我在apifox中发现对websocket的支持不好,而且在postman中有对socket.io专门的测试。下面带着大家上手试试
经过这些操作我们就拿到了发出的消息
注意要订阅的是newMessage也就是subscribe装饰器的参数
下面介绍另外一种,借助WebsocketServer
import { ConnectedSocket, MessageBody, SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'
import { Server, Socket } from 'socket.io'
@WebSocketGateway({ cors: { origin: '*' } })
export class EventGateway {
@WebSocketServer()
server: Server
@SubscribeMessage('newMessage')
handleMessage(@MessageBody() body: any, @ConnectedSocket() client: Socket,) {
this.server.emit('onMessage', {
msg: 'new Message',
content: body
})
}
}
实现的效果一样的。
利用SocketClient创建客户端
接下来利用postman模拟客户端
新开一个nestjs服务,注意mian.ts中的端口号改一下,别冲突了,创建一个socket模块,跟上面步骤一样
import { Module } from '@nestjs/common';
import { SocketClient } from './socket-client';
@Module({
providers: [SocketClient]
})
export class SocketModule { }
下面在socket-client.ts中创建如下代码
import { Injectable, OnModuleInit } from "@nestjs/common";
import { io, Socket } from 'socket.io-client'
@Injectable()
export class SocketClient implements OnModuleInit {
public socketClient: Socket
constructor() {
this.socketClient = io('http://localhost:3000')
}
onModuleInit() {
this.registerConsumerEvent()
}
private registerConsumerEvent() {
this.socketClient.on('connect', () => {
console.log('connect to gateway');
})
this.socketClient.on('onMessage', (payload: any) => {
console.log('socketClientClass');
console.log(payload);
})
}
}
我们这里构造了一个socketClient客户端, OnMouleInit是nestjs的生命周期函数,就是模块初始化时,我们调用了registerConsumerEvent()函数,在这个函数内,我们做了两件事,第一件事,在连接时,做 了逻辑处理,第二件事,监听了onMessage事件,并且能够别人发布的值,接着利用postman来测试一下
也是输入url连接之后,需要选监听的events,打开listen 做到这样就可以了,我们从第一个服务中发送一条消息试试
可以看到监听到了数据,到此我们已经做完了利用socketClient构造客户端
vue3搭配nestjs实现websocket小demo
首先在前端中导入包
pnpm i socket.io-client
之后我们直接在socket.io的官网拿到vue3模板
import { reactive } from "vue";
import { io } from "socket.io-client";
export const state = reactive
<{
connected: boolean
fooEvents: Array<any>
barEvents: Array<any>
}>
({
connected: false,
fooEvents: [],
barEvents: []
});
const URL = "http://localhost:3000"
export const socket = io(URL);
socket.on("connect", () => {
state.connected = true;
});
socket.on("disconnect", () => {
state.connected = false;
});
socket.on("foo", (...args) => {
state.fooEvents.push(args);
});
socket.on("bar", (...args) => {
state.barEvents.push(args);
});
接着在app.vue中引入
<script setup lang="ts">
import { onBeforeMount, onMounted, onUnmounted, reactive } from 'vue';
import { socket } from './socket'
const chatList = reactive<{
value: string
list: Array<any>
}>({
value: '',
list: []
})
// 组件挂载前让socket连接起来
onBeforeMount(() => {
socket.connect();
});
// 组件挂载完毕完成后,监听onMessage事件
onMounted(() => {
socket.on("onMessage", (e) => {
console.log(e);
chatList.list.push(e.content);
});
});
// 组件销毁时断开连接
onUnmounted(() => {
socket.disconnect();
});
// 点击btn发送socket消息
const handleClick = () => {
socket.emit("newMessage", chatList.value, (e: any) => {
console.log(e);
});
};
</script>
<template>
<div>
<input v-model="chatList.value" />
<button @click="handleClick()" style="margin-left: 12px;">发送</button>
<div >
<ul>
<li v-for="(item, index) in chatList.list" :key="index">{{ item }}</li>
</ul>
</div>
</div>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
到此我们就配置完成了,接下来看看效果吧
到此我们就实现了websocket在nestjs的使用,并且能够和客户端进行连接
写在最后
关于nestjs,最近热度也是在上升,真心希望能够保持下去,因为国内的生态太薄弱了,教程太少了,希望能够慢慢的火起来,多点教程.。创作不易,最后也希望能够给文章来个三连😘
转载自:https://juejin.cn/post/7268539035966308410