Asp.Net Core SignalR的协议协商问题
文章目录
- 前言
- 一、协议协商的原理
- 二、常见的协商问题及解决办法
- 1.跨域资源共享(CORS)问题
- 2.身份验证和授权问题
- 3.传输方式不兼容问题
- 4.路由配置错误
- 5.代理和负载均衡器问题
- 6.自定义协商(高级)
- 总结
前言
在ASP.NET Core SignalR 里,协议协商是客户端和服务器建立连接时的重要步骤
一、协议协商的原理
- SignalR 客户端和服务器在建立连接时,会经历以下协商步骤:
- 客户端向服务器发送一个 HTTP GET 请求,请求路径为/negotiate。
- 服务器返回客户端支持的传输方式(像 WebSocket、Server-Sent Events、长轮询)、访问令牌以及其他配置信息。
- 客户端依据服务器返回的信息,挑选合适的传输方式来建立连接。
二、常见的协商问题及解决办法
1.跨域资源共享(CORS)问题
-
问题表现:客户端在协商请求时,浏览器控制台显示类似No ‘Access-Control-Allow-Origin’ header is present的错误。
-
解决办法:
要保证服务器端已正确配置 CORS。
必须启用 SignalR 特定的 CORS 策略。//跨域 string[] urls = new[] { "http://localhost:5173" }; builder.Services.AddCors(opt => opt.AddDefaultPolicy(builder => builder.WithOrigins(urls).AllowAnyMethod().AllowAnyHeader().AllowCredentials())); app.UseCors(); app.UseHttpsRedirection();
2.身份验证和授权问题
-
问题表现:协商请求返回 401(未授权)或者 403(禁止访问)错误。
-
解决办法:
要确保客户端在协商请求中正确传递了认证信息。
对服务器端的授权策略进行检查。// 对Hub添加授权要求 [Authorize] public class MyHubService: Hub {// ... }
3.传输方式不兼容问题
- 问题表现:客户端和服务器没有共同支持的传输方式。
- 解决办法:
检查客户端和服务器是否都支持相同的传输方式。
可以在客户端限制传输方式。// 创建新连接state.connection = new signalR.HubConnectionBuilder().withUrl(state.serverUrl, {//skipNegotiation: true, // 尝试跳过协商步骤transport: signalR.HttpTransportType.WebSockets // 限制传输方式,强制使用 WebSockets}).withAutomaticReconnect({nextRetryDelayInMilliseconds: retryContext => {state.retryCount = retryContext.previousRetryCount + 1;return Math.min(1000 * Math.pow(2, state.retryCount), 30000);}}).configureLogging(signalR.LogLevel.Debug) // 启用详细调试日志.build();
4.路由配置错误
- 问题表现:协商请求返回 404(未找到)错误。
- 解决办法:
确认 Hub 的路由配置无误。// 配置路由 app.MapHub<MyHubService>("/Hubs/MyHubService");// SignalR 终结点
5.代理和负载均衡器问题
- 问题表现:在 Kubernetes/IIS ARR 后出现 WebSocket is closed 或协商超时。
- 解决办法:
- 粘性会话(Sticky Sessions):
若使用 WebSocket,需配置负载均衡器保持会话亲和性。 - 转发头配置:
app.UseForwardedHeaders(new ForwardedHeadersOptions {ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto });
- WebSocket 支持
IIS 需启用 WebSocket 模块,Azure App Service 配置 webSockets: true。
- 粘性会话(Sticky Sessions):
6.自定义协商(高级)
- 禁用协商(仅限 WebSocket)
服务器必须启用 WebSocket 并开放正确端口(通常 80/443)。// 创建新连接state.connection = new signalR.HubConnectionBuilder().withUrl(state.serverUrl, {skipNegotiation: true, // 尝试跳过协商步骤transport: signalR.HttpTransportType.WebSockets // 强制使用 WebSockets}).withAutomaticReconnect({nextRetryDelayInMilliseconds: retryContext => {state.retryCount = retryContext.previousRetryCount + 1;return Math.min(1000 * Math.pow(2, state.retryCount), 30000);}}).configureLogging(signalR.LogLevel.Debug) // 启用详细调试日志.build();
总结
-
检查终结点是否注册 MapHub
-
验证 CORS 策略(特别是 AllowCredentials())
-
确保身份认证信息正确传递
-
核对客户端/服务端库版本
-
检查网络层(防火墙、代理、负载均衡)
-
启用详细日志分析协商过程