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

.NET 7 Blazor Server登录/登出时WebSocket重连异常求助

问题排查:Blazor Server登录/登出时WebSocket重连异常

原因分析

  • 登录/登出操作触发AuthenticationStateProvider的NotifyAuthenticationStateChanged方法时,若时机不当(比如WebSocket连接状态不稳定),会引发Blazor SignalR连接冲突,导致连接断开重连。
  • 自定义CustomAuthenticationStateProvider中异步操作处理不严谨,比如身份状态未完全更新就通知状态变更,造成客户端与服务端状态同步异常,触发WebSocket状态错误。
  • 登录/登出逻辑使用了非Blazor原生跳转方式(如window.location.href强制刷新),直接中断当前SignalR连接,Blazor会自动尝试重连从而出现提示。

解决方法

1. 优化CustomAuthenticationStateProvider实现

确保身份状态稳定后再触发状态变更通知,避免异步竞态问题:

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private AuthenticationState _cachedState;

    public CustomAuthenticationStateProvider(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _cachedState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync("/api/auth/currentuser");
            if (response.IsSuccessStatusCode)
            {
                var user = await response.Content.ReadFromJsonAsync<ClaimsPrincipal>();
                _cachedState = new AuthenticationState(user);
            }
            else
            {
                _cachedState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }
        }
        catch
        {
            _cachedState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }
        return _cachedState;
    }

    public void UpdateAuthenticationState(ClaimsPrincipal user)
    {
        _cachedState = new AuthenticationState(user);
        // 确保状态更新完成后再通知客户端
        NotifyAuthenticationStateChanged(Task.FromResult(_cachedState));
    }
}

2. 修正登录/登出的状态更新与导航逻辑

使用Blazor原生状态通知和导航方式,避免强制刷新中断连接:

@page "/login"
@inject CustomAuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavManager
@inject HttpClient Http

<div>
    <input @bind="_username" placeholder="用户名" />
    <input @bind="_password" type="password" placeholder="密码" />
    <button @onclick="LoginAsync">登录</button>
</div>

@code {
    private string _username;
    private string _password;

    private async Task LoginAsync()
    {
        var loginModel = new { Username = _username, Password = _password };
        var response = await Http.PostAsJsonAsync("/api/auth/login", loginModel);
        
        if (response.IsSuccessStatusCode)
        {
            var user = await response.Content.ReadFromJsonAsync<ClaimsPrincipal>();
            // 更新身份状态,由AuthStateProvider同步到客户端
            AuthStateProvider.UpdateAuthenticationState(user);
            // 使用Blazor导航,不强制刷新页面
            NavManager.NavigateTo("/", forceLoad: false);
        }
        else
        {
            // 处理登录失败逻辑,比如提示错误信息
        }
    }
}

3. 登出逻辑对应调整

@inject CustomAuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavManager

<button @onclick="LogoutAsync">登出</button>

@code {
    private async Task LogoutAsync()
    {
        await Http.PostAsync("/api/auth/logout", null);
        // 更新为匿名身份
        AuthStateProvider.UpdateAuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        // Blazor原生导航
        NavManager.NavigateTo("/login", forceLoad: false);
    }
}

4. 检查SignalR Hub配置

确保Program.cs中Blazor Hub的超时配置合理,避免连接提前断开:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options =>
    {
        options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
        options.KeepAliveInterval = TimeSpan.FromSeconds(15);
    });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 22:03:15