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

Blazor Server中ISession不稳定问题求助:会话键值异常

解决Server-Side Blazor中ISession不稳定的问题

问题根源

  1. 预渲染机制触发两次OnInitialized:默认Server-Side Blazor采用ServerPrerendered渲染模式,组件会先在服务器完成预渲染(第一次执行OnInitialized),再建立SignalR连接进行交互式渲染(第二次执行OnInitialized)。预渲染阶段设置的Session值需要等待客户端接收并保存Session Cookie后,才能在后续请求中被读取,这就导致首次刷新时第一次OnInitialized读不到键值。
  2. 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更可靠:

  1. 创建Scoped状态服务:
public class UserStateService
{
    public string Test { get; set; }
    public string Test1 { get; set; }
    public string Test2 { get; set; }
}
  1. 在Program.cs中注册服务:
builder.Services.AddScoped<UserStateService>();
  1. 在组件中注入并使用:
@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:04:52