Blazor页面切换时依赖注入失效,如何实现用户独立会话状态?
搞定Blazor用户会话状态的隔离与共享问题
先说说你遇到的两个坑
- 用
AddScoped时跳转页面状态就没了:在Blazor WebAssembly里,Scoped服务是每个组件实例单独一份,页面跳转等于加载新组件,之前存的状态自然就丢了;要是你用的Blazor Server还出现这问题,大概率是导航时加了强制刷新(forceLoad: true),直接重启了整个应用,Scoped实例肯定会重置。 - 换
AddSingleton又串号:Singleton是全局唯一实例,所有用户共用同一个对象,后面登录的用户自然会覆盖前面的状态,这肯定不符合需求。
分场景给你解决办法
如果你用的是Blazor Server(优先选这个)
Blazor Server里的AddScoped本来就是每个用户会话(也就是每个SignalR连接)单独一份,正常情况下不会丢状态。要是你那状态还是没了,按这几步改:
- 保持
Program.cs里的Scoped注册不变:
builder.Services.AddScoped<SessionState>();
- 登录跳转时别加强制刷新:
state.id = int.Parse(dt.Rows[0]["iduser"].ToString()); state.verified = true; // 就用默认的NavigateTo,别加forceLoad: true NavigationManager.NavigateTo("home");
- 要是非得强制刷新(比如要加载新的静态资源),那得把状态存到
HttpContext.Session里,让SessionState自己去读取:- 先在
Program.cs里加Session支持:builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); builder.Services.AddHttpContextAccessor(); - 然后修改你的
SessionState类,让它和Session绑定:public class SessionState { private readonly IHttpContextAccessor _httpContextAccessor; public SessionState(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; var session = _httpContextAccessor.HttpContext?.Session; if (session != null) { id = int.TryParse(session.GetString("UserId"), out var userId) ? userId : 0; verified = bool.TryParse(session.GetString("IsVerified"), out var verified) ? verified : false; } } private int _id; public int id { get => _id; set { _id = value; _httpContextAccessor.HttpContext?.Session.SetString("UserId", value.ToString()); } } private bool _verified; public bool verified { get => _verified; set { _verified = value; _httpContextAccessor.HttpContext?.Session.SetString("IsVerified", value.ToString()); } } }
- 先在
如果你用的是Blazor WebAssembly
WebAssembly跑在浏览器里,没有服务器端的会话概念,得把状态存在浏览器的存储里,同时封装成Scoped服务让页面共享:
- 先安装NuGet包:
Microsoft.AspNetCore.Components.WebAssembly.Storage。 - 修改
SessionState类,用LocalStorage存状态:
public class SessionState { private readonly ILocalStorageService _localStorage; private int _id; private bool _verified; public SessionState(ILocalStorageService localStorage) { _localStorage = localStorage; // 初始化时从LocalStorage读取之前的状态 LoadStateAsync().Wait(); } public int id { get => _id; set { _id = value; _localStorage.SetItemAsync("UserId", value).Wait(); } } public bool verified { get => _verified; set { _verified = value; _localStorage.SetItemAsync("IsVerified", value).Wait(); } } private async Task LoadStateAsync() { _id = await _localStorage.GetItemAsync<int>("UserId") ?? 0; _verified = await _localStorage.GetItemAsync<bool>("IsVerified") ?? false; } }
- 在
Program.cs里注册服务:
builder.Services.AddScoped<SessionState>(); builder.Services.AddLocalStorageServices();
- 登录代码还是你原来那样就行:
state.id = int.Parse(dt.Rows[0]["iduser"].ToString()); state.verified = true; NavigationManager.NavigateTo("home");
备注:LocalStorage会一直存在浏览器里,就算关了标签页再打开还在;要是想关了浏览器就清状态,换成
ISessionStorageService就行。
最后提俩注意点
- 别往
SessionState里存敏感信息,真要存得加密处理。 - 不管哪种方案,确保页面里注入的是同一个Scoped实例——Blazor Server里是会话级,WebAssembly里是当前标签页级,都是每个用户单独一份的。
内容的提问来源于stack exchange,提问作者Kasra Ghavami
相关产品推荐
相关产品推荐

