本地测试Azure App Service上的SignalR时WebSocket握手返回200错误
SignalR连接Azure App Service失败排查与解决
问题描述
本地测试部署在Azure App Service的SignalR聊天应用时,遇到两类连接错误:
- 开启
skipNegotiation: true时:WebSocket connection to 'wss:
.azurewebsites.net/Chat' failed: Error during WebSocket handshake: Unexpected response code: 200
后端日志:Exception: The underlying connection was closed before the hub handshake could complete. - 关闭
skipNegotiation(设为false)时:connection to signalr Unexpected end of JSON input
环境配置
- 后端:.NET 8 + SignalR
- 前端:Flutter + signalr_core库
- 托管:Azure App Service
- 后端配置:已注册SignalR、映射
/ChatHub、启用全量CORS策略、配置QueryStringUserIdProvider - 前端配置:signalr_core指定WebSocket传输、手动设置协商开关
一、Azure App Service关键配置检查
- 开启WebSockets支持:
进入Azure门户的App Service → 配置 → 常规设置,找到Web sockets选项,设置为开启,保存后重启应用。Azure默认关闭WebSocket,这是导致握手失败返回200的核心原因。 - SignalR服务扩展检查(可选):
若使用Azure SignalR Service(而非自托管SignalR),需在App Service的配置中添加AzureSignalRConnectionString应用设置,同时后端安装Microsoft.Azure.SignalR包,修改注册代码为builder.Services.AddSignalR().AddAzureSignalR();。自托管SignalR可跳过此步,但必须确保WebSocket已开启。
二、后端代码调整
- 修正CORS与中间件配置:
SignalR依赖凭证支持,需在CORS策略中添加AllowCredentials(),同时确保中间件顺序正确:// Program.cs var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR(); builder.Services.AddCors(options => { options.AddPolicy("CorsPolicy", policy => { policy.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // 必须添加,SignalR握手依赖此配置 }); }); var app = builder.Build(); app.UseCors("CorsPolicy"); // 确保CORS在路由之前执行 app.UseRouting(); app.MapHub<ChatHub>("/Chat"); app.Run(); - 验证Hub类实现:
确保ChatHub继承自Hub,无自定义握手拦截逻辑:public class ChatHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
三、Flutter前端代码修正
- 跳过协商模式(
skipNegotiation: true):
跳过协商时,需直接访问SignalR的WebSocket专用端点,路径需追加/ws:// chat_hub_service.dart _connection = HubConnectionBuilder() .withUrl('wss://<your-app>.azurewebsites.net/Chat/ws', HttpConnectionOptions( skipNegotiation: true, transport: HttpTransportType.webSockets, )) .build(); await _connection.start(); - 协商模式(推荐,
skipNegotiation: false):
移除手动指定的传输方式,用https://开头的URL,客户端会自动协商最佳传输:// chat_hub_service.dart _connection = HubConnectionBuilder() .withUrl('https://<your-app>.azurewebsites.net/Chat') .build(); await _connection.start();
四、额外排查步骤
- 先测试本地后端连接:用Flutter客户端连接
ws://localhost:<port>/Chat/ws(跳过协商)或http://localhost:<port>/Chat(协商模式),确认代码逻辑无问题。 - 查看Azure应用日志:进入App Service → 日志 → 应用日志,检查是否有CORS拦截、路由匹配失败等详细错误。
- 排除代理/防火墙干扰:本地测试时确保没有代理或防火墙拦截443端口的
wss://流量。
内容的提问来源于stack exchange,提问作者Mateusz Placha
相关产品推荐
相关产品推荐

