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
相关产品推荐
相关产品推荐

