You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7 Blazor WASM中SignalR为何未使用WebSocket通信?

问题:Blazor WASM SignalR 已建立WebSocket连接却仍使用长轮询通信

我有一个托管式.NET 7 Blazor WASM项目,已与宿主建立SignalR连接且通信正常。但发现尽管WebSocket连接已建立,通信仍采用long-poll + calls方式,连SSE都未使用。当前为开发环境,客户端与服务器同机运行,无WebSocket阻塞情况,且连接选项中已设置AccessTokenProvider。

请问这是为何?与开发环境或WASM有关吗?生产环境也会如此吗?


更新于23-12-27

按照建议添加了客户端日志,结果如下:
客户端日志
(顺带一提:浏览器是Edge……不支持SSE??)

展开的错误信息:
展开的错误信息

服务器端日志:
服务器端日志

从日志来看,连接未被拒绝,仅受到质询。且实际WebSocket连接已携带所需令牌:
WebSocket携带令牌

客户端代码:

客户端代码

服务器代码:

服务器代码1
服务器代码2
服务器代码3
服务器代码4


分析与解决

1. 核心原因:SSE传输因令牌问题失败触发降级

从客户端错误日志可见Failed to construct 'EventSource',Edge完全支持SSE,问题出在身份验证的传输限制:

  • SSE受浏览器安全限制,无法在请求头中携带Authorization Bearer令牌,导致客户端尝试SSE连接时,因无有效令牌被服务器质询,最终SSE连接失败。
  • SignalR的传输协商逻辑会在某一传输失败时自动降级到下一种可用方式,SSE失败后就降级到了长轮询;即便WebSocket连接本身能成功,协商阶段的失败已经触发了降级流程。

2. 与开发环境/WASM的关联

  • 和开发环境本身无关,但本地身份验证配置(比如HTTPS、令牌传递逻辑)更容易暴露这类浏览器限制问题。
  • WASM本身不限制WebSocket/SSE,问题根源是浏览器对SSE的请求头限制,和WASM runtime无关。

3. 生产环境的表现

如果不修复SSE的令牌传递问题,生产环境会出现同样的降级情况。可通过以下两种方式解决:

方案一:配置SSE通过查询字符串传递令牌

SSE支持从查询字符串读取令牌,需要同时修改客户端和服务器端:

  • 客户端配置:确保SignalR为SSE传输启用查询字符串传令牌
hubConnection = new HubConnectionBuilder()
    .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"), options =>
    {
        options.AccessTokenProvider = async () =>
        {
            var accessTokenResult = await AccessTokenProvider.RequestAccessToken();
            accessTokenResult.TryGetToken(out var token);
            return token?.Value;
        };
        // 显式指定允许的传输类型
        options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
    })
    .Build();
  • 服务器端配置:允许从查询字符串读取JWT令牌
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // 保留原有TokenValidationParameters配置
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                var accessToken = context.Request.Query["access_token"];
                var path = context.HttpContext.Request.Path;
                // 针对SignalR Hub路径,从查询字符串取令牌
                if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chathub"))
                {
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    });

方案二:强制优先使用WebSocket

如果不需要兼容SSE/长轮询,可直接指定仅使用WebSocket传输,跳过降级逻辑:

options.Transports = HttpTransportType.WebSockets;

WebSocket支持在请求头携带Authorization令牌,不会出现SSE的限制问题,能稳定保持WebSocket连接。


内容的提问来源于stack exchange,提问作者ZorgoZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:45:00