React对接ASP.NET Core SignalR获取活跃连接数报错
React对接ASP.NET Core SignalR协商失败问题修复
问题表现
React前端连接ASP.NET Core后端SignalR Hub时抛出错误:
Error: Failed to complete negotiation with the server: TypeError: Failed to fetch
修复步骤
1. 补全后端CORS配置
SignalR跨域请求对CORS有明确要求,必须允许凭证,且Origin必须精确匹配前端地址。在ASP.NET Core的Program.cs中添加如下配置:
// 服务注册阶段添加CORS策略 builder.Services.AddCors(options => { options.AddPolicy("SignalRAllowFront", policy => { policy.WithOrigins("http://localhost:3000") // 替换为React前端实际运行地址,不要带末尾斜杠 .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // SignalR强制要求开启该项 }); }); // 中间件配置阶段,注意顺序:UseRouting之后,UseAuthorization、MapHub之前调用 app.UseCors("SignalRAllowFront"); // Hub路由配置保持原有即可 endpoints.MapHub<OnConnectionHub>("hubs/onConnectionHub");
注意:如果前端用HTTPS、端口不是3000,必须把Origin改成和浏览器地址栏里前端地址完全一致,包括协议、域名、端口三部分。
2. 修正后端Hub的异步阻塞写法
原有代码里用.GetAwaiter().GetResult()属于同步阻塞异步代码的写法,高并发下会引发线程池死锁,改成标准async/await写法:
public class OnConnectionHub : Hub { public static int ConnectionCount { get; set; } public override async Task OnConnectedAsync() { ConnectionCount++; await Clients.All.SendAsync("updateConnectionCount", ConnectionCount); await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception? exception) { ConnectionCount--; await Clients.All.SendAsync("updateConnectionCount", ConnectionCount); await base.OnDisconnectedAsync(exception); } }
3. 修复React组件连接逻辑
原有代码把连接创建、事件绑定、启动逻辑直接写在组件函数体中,每次组件重渲染都会重复创建连接、重复绑定事件,是前端侧的核心问题。将连接逻辑移入useEffect,保证只在组件挂载时执行一次,卸载时清理连接:
import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr"; import { useState, useEffect } from "react"; export const ConnectionCount = () => { const [connectionCount, setConnectionCount] = useState(0); useEffect(() => { // 创建连接实例 const connection = new HubConnectionBuilder() .withUrl("https://localhost:7199/hubs/onConnectionHub") .configureLogging(LogLevel.Information) // 开启调试日志,方便排查问题 .withAutomaticReconnect() // 可选配置,开启自动重连 .build(); // 绑定服务端推送事件 connection.on("updateConnectionCount", (count) => { setConnectionCount(count); }); // 启动连接并处理异常 const startConnect = async () => { try { await connection.start(); } catch (err) { console.error("连接失败,5秒后重试", err); setTimeout(startConnect, 5000); } }; startConnect(); // 组件卸载时关闭连接,清理资源 return () => { connection.stop(); }; }, []); // 空依赖数组,仅在组件首次挂载时执行 return <p>Active members {connectionCount}</p>; };
排查验证
改完后如果仍有问题,按以下顺序验证:
- 直接在浏览器访问
https://localhost:7199/hubs/onConnectionHub/negotiate?negotiateVersion=1,如果返回404,检查Hub路由配置和中间件顺序是否正确。 - 如果浏览器控制台明确报CORS错误,重新检查CORS配置的Origin是否完全匹配,是否遗漏
AllowCredentials()配置。 - 如果后端用HTTPS,确认本地开发证书已被系统信任,访问后端地址没有证书风险提示。
内容的提问来源于stack exchange,提问作者Joro
相关产品推荐
相关产品推荐

