.NET5 Blazor Server集成SignalR+Azure AD统计在线用户报错
抛出JsonReaderException: '<' is an invalid start of a value和InvalidDataException: Invalid negotiation response received的核心原因是:SignalR客户端发起连接协商(negotiate)请求时,没有收到预期的JSON格式响应,反而收到了HTML格式内容(通常是404错误页、认证重定向页,这类内容都以<html>标签开头,System.Text.Json读取到第一个字符<就会直接抛出解析错误)。
你现有代码里有两处直接触发该问题的错误:
- 前后端Hub路径不匹配:服务端Startup中映射的Hub地址是
/peoplehub,但前端组件初始化连接时写的地址是/chathub,请求打到不存在的路由,服务端返回HTML格式的404页面,直接触发解析失败。 - Hub类名拼写错误:定义Hub时类名写为
PeoplHub(缺失字母e),和MapHub泛型参数传入的PeopleHub不一致,即便路径修正也无法匹配到正确的Hub实例。
1. 修正命名、路径不匹配问题
首先修正Hub类的拼写错误,确保类名和服务端注册时的泛型参数完全一致:
// 补上类名缺失的字母e,添加public访问修饰符 public class PeopleHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
然后修改Index.razor中的Hub连接地址,和服务端映射路径保持一致:
hubConnection = new HubConnectionBuilder() // 将错误的/chathub替换为服务端映射的/peoplehub .WithUrl(NavigationManager.ToAbsoluteUri("/peoplehub")) .Build(); hubConnection.On<string, string>("ReceiveMessage", (user, message) => { var encodedMsg = $"{user}: {message}"; messages.Add(encodedMsg); InvokeAsync(StateHasChanged); }); await hubConnection.StartAsync();
2. 修复Azure AD认证拦截问题
你的站点全站启用了Azure AD认证,未携带有效认证凭据的请求会被重定向到Azure AD登录页(同样是HTML格式),也会触发解析错误。如果要求只有登录用户才能连接Hub,不需要给Hub加匿名访问,只需要在构建连接时配置携带认证凭据,同时调整中间件顺序:
- 确保
app.UseResponseCompression()放在所有其他业务中间件的最前面(仅放在异常处理中间件之后),避免压缩逻辑干扰SignalR响应格式。 - 给Hub路由配置认证规则,避免未登录请求被302重定向到登录页:
app.UseEndpoints(endpoints => { // 原有MapHub配置补充认证规则,不要加AllowAnonymous endpoints.MapHub<PeopleHub>("/peoplehub").RequireAuthorization(); // 其他原有路由配置... });
- 客户端构建Hub连接时开启凭据携带:
hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/peoplehub"), options => { options.UseDefaultCredentials = true; }) .Build();
3. 优化连接释放逻辑
现有DisposeAsync实现没有判断连接状态,连接处于连接中/断开中状态时直接释放会抛出异常,补充状态判断:
public async ValueTask DisposeAsync() { if (hubConnection is not null) { if (hubConnection.State == HubConnectionState.Connected || hubConnection.State == HubConnectionState.Connecting) { await hubConnection.StopAsync(); } await hubConnection.DisposeAsync(); } }
修改完成后重启应用,打开浏览器开发者工具的网络面板,筛选negotiate请求,确认该请求响应状态为200,响应内容为包含connectionId、availableTransports字段的JSON格式,即表示连接正常,不会再抛出之前的解析错误。
内容的提问来源于stack exchange,提问作者redar ismail

