当前位置: 首页 > article >正文

Vue3项目里SignalR怎么用?一个聊天室Demo带你从配置到上线(.NET 6 + Vue 3)

Vue3与SignalR实战构建高互动聊天室的全栈指南引言在当今追求实时交互体验的Web应用中传统的HTTP请求-响应模式已无法满足即时通讯、实时通知等场景需求。SignalR作为ASP.NET Core生态中的实时通信库通过自动选择最佳传输协议WebSocket、Server-Sent Events或长轮询为开发者提供了简洁高效的解决方案。结合Vue3的响应式特性我们可以构建出媲美原生应用体验的实时功能。本文将带领你从零开始使用.NET 6和Vue3构建一个功能完备的聊天室应用。不同于基础教程我们会深入探讨以下实战要点如何优雅处理Vue组件与SignalR的状态同步基于JWT的身份验证在实时连接中的实现使用Pinia管理跨组件共享的聊天状态生产环境部署时Nginx的WebSocket配置技巧1. 环境搭建与项目初始化1.1 创建.NET 6 Web API项目首先使用Visual Studio或dotnet CLI创建新项目dotnet new webapi -n ChatServer cd ChatServer安装必要的SignalR NuGet包dotnet add package Microsoft.AspNetCore.SignalR.Client在Program.cs中配置SignalR服务var builder WebApplication.CreateBuilder(args); // 添加SignalR服务并配置JSON序列化 builder.Services.AddSignalR() .AddJsonProtocol(options { options.PayloadSerializerOptions.PropertyNamingPolicy null; }); // 添加跨域策略开发环境 builder.Services.AddCors(options { options.AddPolicy(DevCors, policy { policy.WithOrigins(http://localhost:8080) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); var app builder.Build(); app.UseCors(DevCors); app.MapHubChatHub(/chatHub);1.2 初始化Vue3项目使用Vite快速搭建Vue3项目npm create vitelatest chat-client --template vue-ts cd chat-client npm install microsoft/signalr pinia axios配置开发服务器代理vite.config.tsexport default defineConfig({ server: { proxy: { /chatHub: { target: http://localhost:5000, ws: true, changeOrigin: true } } } })2. 核心功能实现2.1 设计聊天中心Hub创建ChatHub.cs实现核心聊天逻辑public class ChatHub : Hub { private static readonly Dictionarystring, UserInfo _connections new(); public override async Task OnConnectedAsync() { var httpContext Context.GetHttpContext(); var token httpContext?.Request.Query[access_token]; if (!string.IsNullOrEmpty(token)) { var user ValidateJwtToken(token); if (user ! null) { _connections[Context.ConnectionId] user; await Clients.All.SendAsync(UserConnected, user); } } } public async Task SendMessage(string room, ChatMessage message) { if (_connections.TryGetValue(Context.ConnectionId, out var sender)) { message.Sender sender.Name; message.Timestamp DateTime.UtcNow; await Clients.Group(room).SendAsync(ReceiveMessage, message); } } public async Task JoinRoom(string room) { await Groups.AddToGroupAsync(Context.ConnectionId, room); } public override async Task OnDisconnectedAsync(Exception? exception) { if (_connections.TryGetValue(Context.ConnectionId, out var user)) { _connections.Remove(Context.ConnectionId); await Clients.All.SendAsync(UserDisconnected, user.Id); } } }2.2 Vue3客户端集成创建SignalR服务封装src/services/signalR.tsimport { HubConnectionBuilder, LogLevel } from microsoft/signalr; const createConnection (url: string, token?: string) { return new HubConnectionBuilder() .withUrl(url, { accessTokenFactory: () token || , skipNegotiation: true, transport: HttpTransportType.WebSockets }) .configureLogging(LogLevel.Information) .withAutomaticReconnect({ nextRetryDelayInMilliseconds: (context) { return Math.min(context.elapsedMilliseconds * 2, 10000); } }) .build(); }; export const useChatConnection () { const connection refHubConnection(); const isConnected ref(false); const start async (token?: string) { connection.value createConnection(/chatHub, token); connection.value.onclose(() { isConnected.value false; }); try { await connection.value.start(); isConnected.value true; } catch (err) { console.error(Connection failed:, err); } }; return { connection, isConnected, start }; };2.3 状态管理设计使用Pinia管理聊天状态src/stores/chat.tsimport { defineStore } from pinia; interface Message { id: string; content: string; sender: string; timestamp: Date; } export const useChatStore defineStore(chat, { state: () ({ currentRoom: general, messages: [] as Message[], onlineUsers: [] as UserInfo[], connectionId: }), actions: { addMessage(message: Message) { this.messages.push(message); // 保持消息列表不超过100条 if (this.messages.length 100) { this.messages.shift(); } }, setUsers(users: UserInfo[]) { this.onlineUsers users; }, setConnectionId(id: string) { this.connectionId id; } } });3. 高级功能实现3.1 私聊与房间管理扩展Hub支持私聊功能public async Task SendPrivateMessage(string targetUserId, ChatMessage message) { if (_connections.TryGetValue(Context.ConnectionId, out var sender)) { message.Sender sender.Name; message.IsPrivate true; var target _connections.FirstOrDefault(x x.Value.Id targetUserId); if (!string.IsNullOrEmpty(target.Key)) { await Clients.Client(target.Key).SendAsync(ReceivePrivateMessage, message); await Clients.Caller.SendAsync(ReceivePrivateMessage, message); } } }3.2 消息持久化与历史记录集成Entity Framework Core保存聊天记录public class ChatDbContext : DbContext { public DbSetPersistedMessage Messages { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.EntityPersistedMessage() .HasIndex(m m.Room); } } public async TaskListPersistedMessage GetMessageHistory(string room, int count 20) { return await _dbContext.Messages .Where(m m.Room room) .OrderByDescending(m m.Timestamp) .Take(count) .ToListAsync(); }4. 生产环境部署4.1 Nginx配置优化针对WebSocket连接的Nginx配置server { listen 80; server_name yourdomain.com; location / { proxy_pass http://localhost:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; } location /chatHub { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }4.2 连接健康监测实现心跳检测机制// 客户端心跳 setInterval(() { if (connection.value?.state Connected) { connection.value.invoke(Ping); } }, 30000); // 服务端超时处理 services.AddSignalR(options { options.ClientTimeoutInterval TimeSpan.FromSeconds(60); options.KeepAliveInterval TimeSpan.FromSeconds(30); });5. 安全与性能优化5.1 JWT认证集成增强的Hub认证中间件[Authorize(AuthenticationSchemes JwtBearerDefaults.AuthenticationScheme)] public class ChatHub : Hub { public override async Task OnConnectedAsync() { var userId Context.User?.FindFirstValue(ClaimTypes.NameIdentifier); // ...连接逻辑 } }5.2 消息限流与防刷实现消息速率限制public class RateLimitFilter : IHubFilter { private static readonly ConcurrentDictionarystring, DateTime _lastMessageTimes new(); public async ValueTaskobject? InvokeMethodAsync( HubInvocationContext invocationContext, FuncHubInvocationContext, ValueTaskobject? next) { var methodName invocationContext.HubMethodName; if (methodName SendMessage) { var connectionId invocationContext.Context.ConnectionId; if (_lastMessageTimes.TryGetValue(connectionId, out var lastTime)) { if (DateTime.UtcNow - lastTime TimeSpan.FromSeconds(1)) { throw new HubException(消息发送过于频繁); } } _lastMessageTimes[connectionId] DateTime.UtcNow; } return await next(invocationContext); } }在项目开发过程中我发现SignalR的连接稳定性对用户体验至关重要。特别是在移动端场景下网络切换时自动重连机制的实现需要格外注意。建议在客户端实现渐进式重试策略初始重试间隔较短随后逐渐增加直到达到最大间隔。同时对于关键业务消息应考虑实现客户端消息队列和确认机制确保消息不丢失。

相关文章:

Vue3项目里SignalR怎么用?一个聊天室Demo带你从配置到上线(.NET 6 + Vue 3)

Vue3与SignalR实战:构建高互动聊天室的全栈指南 引言 在当今追求实时交互体验的Web应用中,传统的HTTP请求-响应模式已无法满足即时通讯、实时通知等场景需求。SignalR作为ASP.NET Core生态中的实时通信库,通过自动选择最佳传输协议&#xff0…...

C++位运算技巧应用

C位运算技巧应用位运算是直接操作二进制位的运算,具有极高的执行效率。掌握位运算技巧可以优化算法性能并实现紧凑的数据表示。基本位运算包括与、或、异或、取反和移位操作。#include #includevoid basic_bitwise_operations() { unsigned int a 0b1010; unsigned…...

ROS Noetic下,5分钟搞定Hector SLAM建图(附避坑指南与完整launch文件)

ROS Noetic下Hector SLAM极速建图实战:从零到地图生成的避坑全指南 刚接触ROS和SLAM的开发者往往被复杂的配置和概念淹没,而Hector SLAM作为最轻量级的激光建图方案,却能在5分钟内让你看到实实在在的建图效果。本文将采用逆向教学法——先带你…...

Sunshine游戏串流快速上手:3步搭建你的个人云游戏服务器

Sunshine游戏串流快速上手:3步搭建你的个人云游戏服务器 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 想要在任何设备上玩转PC游戏大作吗?Sunshine作为一…...

别再只算差异了!用Cytoscape给Hub Gene分析加个‘可视化Buff’(附脑网络实战图)

别再只算差异了!用Cytoscape给Hub Gene分析加个‘可视化Buff’(附脑网络实战图) 在生物信息学分析中,差异基因筛选往往是研究的第一步,但如何从海量差异基因中找出真正具有生物学意义的"关键调控者"&#xf…...

仅限前500名设计师获取:Midjourney布料质感参数黄金比例表(含棉/丝/涤纶/羊绒/灯芯绒/牛仔布6大基材ISO 105-X12标准映射值)

更多请点击: https://intelliparadigm.com 第一章:Midjourney布料质感模拟的底层逻辑与设计哲学 Midjourney 并非传统三维渲染引擎,其布料质感生成本质上是基于大规模图像-文本对齐模型(CLIP-guided diffusion)的跨模…...

别只懂SARA归档删除!SAP数据生命周期管理实战:归档、查询与长期保留指南

SAP数据生命周期管理实战:从归档策略到长期可查询架构 在数字化转型浪潮中,企业数据量呈现指数级增长。某跨国制造企业的SAP系统仅物料凭证表每年就新增超过200万条记录,导致月结操作耗时从2小时延长至8小时。这不仅是存储空间的问题——系统…...

别再手动画图了!用Mermaid+Markdown在VSCode里5分钟搞定UML设计文档

用文本驱动设计:现代开发者的UML高效实践指南 在技术文档中清晰表达系统设计是每个开发者的必修课。传统UML工具往往需要频繁切换鼠标键盘,拖拽调整元素位置,保存后再手动插入文档——这种工作流不仅低效,更让设计文档与代码库脱节…...

AI安全中的门控发布机制与能力验证实践

我不能按照您的要求生成关于“TAI #200: Anthropic’s Mythos Capability Step Change and Gated Release”的博文内容。原因如下:该标题中出现的“TAI”(通常指The AI Index或Technical AI Safety相关报告编号)、“Anthropic”(一…...

从单机到团队协作:手把手教你用SVN在Windows上搭建个人小型项目版本库(含汉化与日常使用图解)

从单机到团队协作:Windows环境下SVN轻量化部署与实战指南 在个人开发和小型团队协作中,版本控制是保证代码安全和团队高效协作的基石。对于Windows平台的开发者而言,SVN(Subversion)以其简单可靠的特点,成为…...

瑞芯微RK3568音频调试实战:从procfs到i2cset,手把手教你排查I2S无声问题

RK3568音频调试实战:从无声到有声的完整排查指南 当你在RK3568平台上遇到音频输出无声的问题时,那种挫败感是每个嵌入式工程师都深有体会的。本文将以一个真实的调试案例为线索,带你走完从问题定位到最终解决的完整流程,而不仅仅是…...

告别单片机C语言:用FlexLua和CH9329模块5分钟自制USB自动化小工具

零代码革命:用FlexLuaCH9329打造办公自动化神器 每天重复点击鼠标、敲击键盘的枯燥操作是否让你疲惫不堪?想象一下,早晨电脑自动打卡、会议自动记录、邮件自动回复——这些看似需要专业编程知识的自动化操作,现在只需5分钟就能实现…...

Medium作者收益预测模型:轻量可解释的写作价值评估系统

1. 项目概述:这不是一个“预测收入”的模型,而是一套写作者价值评估系统你点开这个标题,第一反应可能是:“哦,又一个用机器学习算稿费的工具?”——但实际远不止如此。Medium writer earnings(M…...

构图不是靠感觉!用Fitts定律+格式塔原理验证的Midjourney 6大构图公式(附Python自动构图评分脚本)

更多请点击: https://kaifayun.com 第一章:构图不是靠感觉!用Fitts定律格式塔原理验证的Midjourney 6大构图公式(附Python自动构图评分脚本) 构图绝非主观直觉,而是可量化、可验证的视觉认知工程。我们基于…...

基于Windows Defender遥测数据与机器学习预测恶意软件感染风险

1. 项目概述:当Windows Defender遇见机器学习在网络安全这个没有硝烟的战场上,恶意软件(Malware)始终是悬在个人用户和企业头顶的达摩克利斯之剑。从勒索软件加密关键文件,到间谍软件窃取商业机密,每一次成…...

【Midjourney印象派风格创作指南】:20年AI视觉专家亲授5大核心参数调优法,3步生成莫奈级画作

更多请点击: https://kaifayun.com 第一章:印象派美学与AI生成的底层耦合逻辑 印象派绘画摒弃精确轮廓与固有色,转而捕捉瞬时的光色颤动、视觉暂留与感知模糊性——这种对“未完成感”“概率性呈现”和“感知优先于表征”的推崇,…...

从Bloodshed到Embarcadero:老牌轻量IDE Dev-C++还值得C++新手用吗?

从Bloodshed到Embarcadero:Dev-C在2024年仍是C新手的理想选择吗? 在C开发工具百花齐放的今天,一个诞生于2000年的轻量级IDE——Dev-C,历经Bloodshed、Orwell到Embarcadero的迭代,依然活跃在部分开发者的工具链中。对于…...

Unity项目性能优化实战:除了Simplygon,还有哪些轻量级减面工具和技巧?

Unity项目性能优化实战:轻量级减面工具与技巧全解析 在Unity项目开发中,3D模型的性能优化是一个永恒的话题。当项目规模扩大、场景复杂度提升时,模型面数往往会成为性能瓶颈的首要因素。Simplygon作为业界知名的减面工具,虽然功能…...

QiMeng-TensorOp:自动生成高性能张量运算代码的框架

1. 项目概述QiMeng-TensorOp是一个革命性的张量算子自动生成框架,它能够基于硬件原语自动生成高性能的张量运算代码。在现代深度学习和大型语言模型(LLMs)中,张量运算如矩阵乘法(GEMM)和卷积(Conv)占据了90%以上的计算量。传统的手动优化方法需要数月时间…...

VAE的隐空间为什么是‘连续’的?一个可视化实验带你理解它与普通自编码器的本质区别

VAE的隐空间连续性:可视化实验揭示生成能力的数学本质 当我们在二维平面上绘制一个螺旋线数据集时,传统自编码器(AE)会将其压缩成一团无序的点云,而变分自编码器(VAE)却能将其映射为一片连贯的星…...

从官方demo到真实项目:手把手教你定制uniapp uni-card卡片的样式与交互

从官方demo到真实项目:手把手教你定制uniapp uni-card卡片的样式与交互 在移动应用开发中,卡片式设计已经成为展示内容的黄金标准。uni-app的uni-card组件为开发者提供了一个快速构建卡片式界面的基础工具,但实际项目中,我们往往需…...

DINOv3特征工程实战:构建可解释、可增量、可部署的CV数据科学工作流

1. 项目概述:这不是又一个ViT教程,而是一份面向实战的数据科学家操作手册“DINOv3 Playbook”这个标题里藏着三个关键信号:DINOv3是Meta最新发布的视觉自监督模型,Playbook不是论文摘要,也不是API文档,而是…...

从V2L到V2G:深度解析双向OBC的HIL测试如何模拟真实用车场景(含CANoe SmartCharging配置)

从露营供电到电网互动:双向OBC的HIL测试实战指南 清晨的山谷里,一辆新能源车静静停驻在营地旁。车主取出便携式电烤盘,将充电枪插入车辆交流充电口,几分钟后烤盘上的牛排开始滋滋作响——这看似简单的场景背后,是双向O…...

DCGAN原理解析:用卷积结构根治GAN模式坍缩

1. 项目概述:从手写数字到逼真猫脸,DCGAN如何让生成模型真正“看见”图像结构你有没有试过训练一个最基础的GAN,结果生成器输出的全是模糊的、像打了马赛克的灰扑扑色块?或者更糟——所有生成的图片都长得一模一样,只是…...

从弹簧小车到悬臂梁:用Python和SymPy手把手推导变分法与欧拉方程

从弹簧小车到悬臂梁:用Python和SymPy手把手推导变分法与欧拉方程 在工程力学和数学物理方程的学习中,变分法是一个既令人着迷又让人望而生畏的领域。它像一座桥梁,连接着抽象的数学原理和具体的物理现象。传统教学中,变分法往往以…...

别再让日志拖慢你的服务器!深入对比C++同步与异步日志的性能差异(附TinyWebServer实测)

C服务器日志性能优化实战:同步与异步方案深度对比 当你的Web服务器开始承载真实流量时,那些看似无害的日志语句可能正在悄悄吞噬着系统性能。我曾在一个电商促销日亲眼目睹,由于同步日志的阻塞导致服务器响应时间从50ms飙升到800ms&#xff0…...

避开这些坑,你的Kalibr标定结果才靠谱:数据采集与质量评估实战

避开这些坑,你的Kalibr标定结果才靠谱:数据采集与质量评估实战 在视觉SLAM和三维重建领域,相机标定的精度直接影响最终系统的性能表现。许多开发者虽然能够按照教程完成Kalibr标定的基本流程,却常常对结果质量缺乏判断依据。本文将…...

别再折腾超级密码了!2024年电信光猫改桥接,打这个电话最快(附完整话术)

2024年电信光猫改桥接最省心方案:一通电话搞定全流程 去年帮邻居调试网络时,发现一个有趣的现象——十个尝试自己破解光猫超级密码的用户里,有九个会卡在第一步。不是密码失效就是界面改版,最后不得不求助运营商。这让我意识到&am…...

DETR训练总找不到目标边界?手把手拆解Conditional DETR的cross-attention,教你精准定位

DETR训练中目标边界定位难题的深度解析与Conditional DETR实战指南 当你在训练DETR模型时,是否经常遇到模型在早期阶段难以准确捕捉目标边界的问题?比如大象的鼻子、斑马的蹄子这些关键部位总是模糊不清。这种现象背后隐藏着DETR架构中一个深层次的设计问…...

别再死记公式了!用Cadence仿真带你直观理解比较器的增益、失调与噪声

Cadence实战:用仿真可视化比较器的增益、失调与噪声特性 刚接触模拟电路设计时,那些复杂的公式和抽象概念总让人头疼。比较器的增益、失调电压、噪声——这些名词在教科书上看起来冰冷生硬,但当你第一次在Cadence Virtuoso中看到它们如何真实…...