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

Blazor Server调用REST后ProtectedSessionStorage.SetAsync抛任务取消异常

问题分析与解决方案

核心问题点

  1. async void 导致的上下文丢失
    Blazor Server中,事件处理方法使用async void会破坏组件的同步上下文,后续调用ProtectedSessionStorage.SetAsync(依赖JS互操作)时,可能因为上下文已失效导致任务被取消。

  2. 类型不匹配错误
    你的LoginSync方法返回AuthenticatedUserResponse?,但Login方法中用TokenContainer类型接收,这会导致编译错误或运行时类型转换异常,间接引发后续操作失败。

  3. 异常被静默吞掉
    LoginSync中捕获所有异常并返回null,无法排查登录请求过程中是否出现网络错误、序列化失败等问题,掩盖了真实故障原因。

  4. 组件状态检查缺失
    如果登录请求耗时较长,组件可能已被销毁,此时调用ProtectedSessionStorage会触发JS互操作超时,抛出任务取消异常。

修复步骤

1. 将async void改为async Task

Blazor事件处理必须使用async Task来维持上下文:

private async Task Login()
{
    var authResponse = await LoginService.LoginSync(VM.Username, VM.Password);
    if (authResponse != null && !IsDisposed)
    {
        await ProtectedSessionStorage.SetAsync("access", authResponse.AccessToken);
        // 登录成功后的逻辑,比如跳转至主页
    }
    else
    {
        // 处理登录失败场景,比如提示用户
    }
}

2. 统一类型匹配

确保LoginSync返回的类型与接收端一致,删除冗余的TokenContainer类,直接使用AuthenticatedUserResponse:

// 删除不必要的TokenContainer类,确保AuthenticatedUserResponse包含AccessToken和RefreshToken
public class AuthenticatedUserResponse
{
    public string AccessToken { get; set; } = string.Empty;
    public string RefreshToken { get; set; } = string.Empty;
}

3. 修复异常处理逻辑

不要静默吞掉所有异常,改为针对性捕获并抛出/记录,便于排查问题:

internal async Task<AuthenticatedUserResponse?> LoginSync(string username, string password)
{
    try
    {
        var request = new LoginRequest() { Password = password, Username = username };
        var stringContent = GetContentFromContainer(request);
        var response = await this.Client.PostAsync($"{BASE_ROUTE}/{LOGIN_ACTION}", stringContent);
        
        // 主动抛出HTTP错误,避免返回null掩盖问题
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<AuthenticatedUserResponse>(content, JsonOptions);
    }
    catch (HttpRequestException ex)
    {
        // 记录日志(如注入ILogger),或返回null并提示用户
        // _logger.LogError(ex, "登录请求失败,状态码:{StatusCode}", ex.StatusCode);
        return null;
    }
    catch (JsonException ex)
    {
        // 处理序列化失败
        // _logger.LogError(ex, "登录响应序列化失败");
        return null;
    }
}

4. 检查组件活跃状态

调用ProtectedSessionStorage前,通过IsDisposed属性确认组件未被销毁,避免JS互操作超时:

if (authResponse != null && !IsDisposed)
{
    await ProtectedSessionStorage.SetAsync("access", authResponse.AccessToken);
}

5. 确认依赖注入正确性

确保ProtectedSessionStorage和LoginService已正确注入组件:

[Inject]
protected ProtectedSessionStorage ProtectedSessionStorage { get; set; } = default!;

[Inject]
protected LoginService LoginService { get; set; } = default!;

额外注意事项

  • 如果登录接口响应耗时超过5秒(Blazor JS互操作默认超时),可以在Program.cs中调整超时时间:
    builder.Services.AddServerSideBlazor()
        .AddHubOptions(options =>
        {
            options.ClientTimeoutInterval = TimeSpan.FromSeconds(10);
            options.HandshakeTimeout = TimeSpan.FromSeconds(10);
        });
    
  • 确保ProtectedSessionStorage的调用在组件的生命周期内(如OnInitializedAsync、事件处理方法),不要在后台线程中调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:18:28