Blazor 8通知服务实现报错:Connection closed with an error
问题:Blazor 8通知服务SignalR报错及轮询方案异常
一、问题背景
使用Blazor 8开发通知服务,需求是从任意Razor页面推送消息至MainLayout页面更新通知计数,但持续触发framework/blazor.server.js报错:Server returned an error on close: Connection closed with an error;同时登录用户后页面陷入无限循环。
二、完整报错追踪
Completed connection handshake. Using HubProtocol 'blazorpack'. dbug: Microsoft.AspNetCore.SignalR.HubConnectionHandler[2] Error when processing requests. System.IO.InvalidDataException: Reading 'target' as String failed. ---> System.MissingMethodException: Method not found: 'System.String Microsoft.AspNetCore.SignalR.IInvocationBinder.GetTarget(System.ReadOnlySpan`1<Byte>)'. at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocolWorker.ReadString(MessagePackReader& reader, IInvocationBinder binder, String field) --- End of inner exception stack trace --- at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocolWorker.ReadString(MessagePackReader& reader, IInvocationBinder binder, String field) at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocolWorker.CreateInvocationMessage(MessagePackReader& reader, IInvocationBinder binder, Int32 itemCount) at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocolWorker.TryParseMessage(ReadOnlySequence`1& input, IInvocationBinder binder, HubMessage& message) at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.DispatchMessagesAsync(HubConnectionContext connection) at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
三、当前实现代码
Program.cs 配置
builder.Services.AddSingleton<IDataAccess, DataAccess>(); builder.Services.AddSingleton<INotificationService, NotificationService>(); // 添加NotificationService // Identity配置 builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>(); builder.Services.AddSignalR(e => { e.EnableDetailedErrors = true; e.MaximumReceiveMessageSize = 102400000; });
MainLayout.razor 核心逻辑
@using Microsoft.AspNetCore.Components.Authorization @inject AuthenticationStateProvider AuthenticationStateProvider @inject INotificationService NotificationService @inject IJSRuntime JS @inject NavigationManager Navigation private int NotificationCount = 0; // 通知数量(初始为0) private string userId; private bool ShowNotificationPopup { get; set; } = false; protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity.IsAuthenticated) { userId = user.FindFirst(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; await PollNotifications(); } } private void ToggleNotificationPopup() { // 切换通知弹窗显示状态 ShowNotificationPopup = !ShowNotificationPopup; } private async Task PollNotifications() { while (true) { await Task.Delay(5000); // 每5秒轮询一次 var notifications = await NotificationService.GetUnreadNotificationsAsync(userId); NotificationCount = notifications.Count; StateHasChanged(); } }
四、解决方案
1. 修复SignalR版本兼容问题
报错中的MissingMethodException是典型的版本不匹配问题,Blazor 8依赖的SignalR相关包必须与ASP.NET Core 8版本完全一致:
- 检查所有Microsoft.AspNetCore.SignalR相关NuGet包(包括Server、Client、MessagePack)版本,确保均为8.x.x
- 清理项目bin/obj目录,重新生成解决方案
2. 解决轮询导致的无限循环问题
当前PollNotifications中的while(true)会阻塞组件生命周期,导致页面无响应,改用Timer替代:
private Timer _notificationTimer; protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity.IsAuthenticated) { userId = user.FindFirst(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; // 初始化Timer,立即执行一次,之后每5秒执行 _notificationTimer = new Timer(async _ => await UpdateNotificationCount(), null, TimeSpan.Zero, TimeSpan.FromSeconds(5)); } } private async Task UpdateNotificationCount() { var notifications = await NotificationService.GetUnreadNotificationsAsync(userId); // 在Blazor线程上下文更新UI await InvokeAsync(() => { NotificationCount = notifications.Count; StateHasChanged(); }); } // 组件销毁时释放Timer资源 public void Dispose() { _notificationTimer?.Dispose(); }
注意:需让MainLayout实现IDisposable接口,确保组件销毁时释放Timer。
3. 轻量替代SignalR的推送方案
若不想依赖SignalR,可使用IAsyncEnumerable结合Blazor的StreamRendering实现实时更新:
- 在NotificationService中添加流式获取通知计数的方法:
public async IAsyncEnumerable<int> GetUnreadNotificationCountStream(string userId) { while (true) { await Task.Delay(5000); var count = (await GetUnreadNotificationsAsync(userId)).Count; yield return count; } }
- 在MainLayout中接收流:
@attribute [StreamRendering(true)] // 其他代码不变... protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity.IsAuthenticated) { userId = user.FindFirst(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; await foreach (var count in NotificationService.GetUnreadNotificationCountStream(userId)) { NotificationCount = count; StateHasChanged(); } } }
内容的提问来源于stack exchange,提问作者Benoît S
相关产品推荐
相关产品推荐

