Blazor Server中ISession不稳定问题求助:会话键值异常
解决Server-Side Blazor中ISession不稳定的问题
问题根源
- 预渲染机制触发两次
OnInitialized:默认Server-Side Blazor采用ServerPrerendered渲染模式,组件会先在服务器完成预渲染(第一次执行OnInitialized),再建立SignalR连接进行交互式渲染(第二次执行OnInitialized)。预渲染阶段设置的Session值需要等待客户端接收并保存Session Cookie后,才能在后续请求中被读取,这就导致首次刷新时第一次OnInitialized读不到键值。 - ISession在Blazor中的生命周期限制:
IHttpContextAccessor获取的HttpContext仅在初始HTTP请求和组件初始渲染阶段有效,后续SignalR交互过程中HttpContext的可靠性无法保证,直接在OnInitialized中操作Session容易出现状态不一致。
解决方案
方案1:禁用预渲染(快速解决)
如果应用不需要预渲染带来的SEO优化,可修改Pages/_Host.cshtml中的组件渲染模式,移除预渲染逻辑:
<!-- 原代码 --> <component type="typeof(App)" render-mode="ServerPrerendered" /> <!-- 修改后 --> <component type="typeof(App)" render-mode="Server" />
修改后组件只会执行一次OnInitialized,Session读写会立即生效,不会出现键值消失的情况。
方案2:保留预渲染,调整Session操作时机
若需保留预渲染,将Session的读写逻辑移至OnAfterRenderAsync方法中——该方法在组件完成客户端渲染后执行,此时HttpContext和Session状态已稳定:
@page "/" @page "/Home" @inject IHttpContextAccessor hca <PageTitle>Home</PageTitle> <h1>Hello, world!</h1> Welcome to your new app. <button @onclick="Click">click</button> @code{ private bool _hasInitialized = false; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !_hasInitialized) { _hasInitialized = true; var session = hca.HttpContext?.Session; if (session != null) { Console.WriteLine($"Home.OnAfterRender - SessionId = {session.Id}"); Console.WriteLine($"Home.OnAfterRender - Keys.Count = {session.Keys.ToList().Count}"); // 读取或初始化Session值 var testValue = session.GetString("test"); if (string.IsNullOrEmpty(testValue)) { session.SetString("test", "tesatassad"); } } } await base.OnAfterRenderAsync(firstRender); } private void Click() { var session = hca.HttpContext?.Session; if (session != null) { session.SetString("test1", "tesatassad"); session.SetString("test2", "tesatassad"); Console.WriteLine($"Home.Click - SessionId = {session.Id}"); Console.WriteLine($"Home.Click - Keys.Count = {session.Keys.ToList().Count}"); } } }
通过firstRender和_hasInitialized标记确保逻辑仅执行一次,同时判断HttpContext是否存在,避免空引用异常。
方案3:使用Blazor专用状态管理替代ISession
对于Server-Side Blazor,更推荐使用Scoped服务存储用户状态,比直接操作ISession更可靠:
- 创建Scoped状态服务:
public class UserStateService { public string Test { get; set; } public string Test1 { get; set; } public string Test2 { get; set; } }
- 在
Program.cs中注册服务:
builder.Services.AddScoped<UserStateService>();
- 在组件中注入并使用:
@page "/" @page "/Home" @inject UserStateService UserState <PageTitle>Home</PageTitle> <h1>Hello, world!</h1> Welcome to your new app. <button @onclick="Click">click</button> @code{ protected override void OnInitialized() { if (string.IsNullOrEmpty(UserState.Test)) { UserState.Test = "tesatassad"; } Console.WriteLine($"Home.Initialized - Test Value = {UserState.Test}"); } private void Click() { UserState.Test1 = "tesatassad"; UserState.Test2 = "tesatassad"; Console.WriteLine($"Home.Click - Test1 Value = {UserState.Test1}"); } }
这种方式将状态直接绑定到用户的SignalR会话,完全避免了Session的不稳定问题。
内容的提问来源于stack exchange,提问作者Franck
相关产品推荐
相关产品推荐

