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

Blazor Server(.NET 8)中认证后通过代码导航导致Scoped状态对象NavParams丢失

Blazor Server(.NET 8)中认证后通过代码导航导致Scoped状态对象NavParams丢失

嘿,我来帮你捋一捋这个问题——我之前在Blazor项目里也碰到过类似的Scoped服务状态丢失的情况,咱们先把问题拆清楚:

先确认下你的场景

首先,正常工作的情况:

  • 你用的是.NET 8的Blazor Web App(InteractiveServer模式),注册了Scoped的NavParams作为跨页面的状态袋:builder.Services.AddScoped<NavParams>()
  • 平时通过用户点击链接/按钮触发NavigationManager.NavigateTo,NavParams里的状态能完美传递到目标页面,一切正常

出问题的场景:

  • 给首页加了@attribute [Authorize]认证属性后,在首页的OnInitializedAsync里设置NavParams.Hotel = new Hotel(),然后直接调用NavigationManager.NavigateTo(HotelRoutes.Details, forceLoad: false)导航
  • 结果到了HotelDetails页面,NavParams.Hotel居然是null!关键差异就是这次导航是代码自动执行的,不是用户点击触发的

问题根源分析

Scoped服务的生命周期是和**当前用户的Blazor电路(Circuit)**绑定的,但认证过程有点特殊:当用户完成认证后,框架可能会重建电路或者重新初始化组件上下文。你在OnInitializedAsync里直接导航,此时NavParams的实例可能还没完全和新的认证后上下文绑定,或者导航触发了组件的重新渲染,导致之前设置的状态被新的Scoped实例覆盖了。

几个可行的解决方案

1. 把导航延迟到组件渲染完成后

试试把导航逻辑移到OnAfterRenderAsync里,这个生命周期方法是在组件第一次渲染完成后执行的,此时状态已经更新,Scoped实例也稳定了:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender) // 确保只执行一次
    {
        NavParams.Hotel = new Hotel();
        await InvokeAsync(StateHasChanged); // 确保状态更新(保险起见)
        NavigationManager.NavigateTo(HotelRoutes.Details, forceLoad: false);
    }
}

2. 用临时存储中转状态

如果认证后电路确实重建了,Scoped实例会被重新创建,这时候可以用SessionStorage临时存一下Hotel信息,到目标页面再取出来:
首先在首页注入IJSRuntime:

[Inject]
public IJSRuntime JSRuntime { get; set; }

protected async override Task OnInitializedAsync()
{
    var hotel = new Hotel();
    // 把Hotel序列化成JSON存到SessionStorage
    await JSRuntime.InvokeVoidAsync("sessionStorage.setItem", "TempHotel", JsonSerializer.Serialize(hotel));
    NavigationManager.NavigateTo(HotelRoutes.Details, forceLoad: false);
}

然后在HotelDetails页面读取并恢复:

[Inject]
public IJSRuntime JSRuntime { get; set; }
[Inject]
public NavParams NavParams { get; set; }

protected async override Task OnInitializedAsync()
{
    var hotelJson = await JSRuntime.InvokeAsync<string>("sessionStorage.getItem", "TempHotel");
    if (!string.IsNullOrEmpty(hotelJson))
    {
        NavParams.Hotel = JsonSerializer.Deserialize<Hotel>(hotelJson);
        // 用完删掉临时存储,避免残留
        await JSRuntime.InvokeVoidAsync("sessionStorage.removeItem", "TempHotel");
    }
}

3. 用查询参数传递关键信息

如果你的Hotel有唯一标识(比如Id),可以把Id作为查询参数传递,到目标页面再加载或者赋值给NavParams:
首页的导航代码:

protected override void OnInitialized()
{
    var hotel = new Hotel { Id = 123 }; // 假设你的Hotel有Id字段
    NavigationManager.NavigateTo($"{HotelRoutes.Details}?hotelId={hotel.Id}", forceLoad: false);
}

HotelDetails页面解析参数:

[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public NavParams NavParams { get; set; }

protected override void OnInitialized()
{
    var uri = new Uri(NavigationManager.Uri);
    var hotelIdStr = uri.Query.Split("hotelId=").LastOrDefault();
    if (int.TryParse(hotelIdStr, out var hotelId))
    {
        // 这里换成你加载Hotel的逻辑,比如从API或者数据库取
        NavParams.Hotel = GetHotelById(hotelId);
    }
}

总结

优先试试第一种延迟导航的方法,因为最简单直接;如果还是不行,说明认证后电路确实重建了,那用临时存储或者查询参数的方法就更可靠。

备注:内容来源于stack exchange,提问作者Terrence

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 08:27:58