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

.NET 8 Blazor中如何加载与存储用户信息?

在Blazor .NET 8中实现类似ASP.NET MVC Session_Start的用户信息加载逻辑

问题背景

此前在ASP.NET MVC中,通过处理Session_Start事件加载用户信息并存入会话变量,避免每次页面加载重复查询数据库。现在想在.NET 8服务器端Blazor(使用Windows身份验证)中实现相同逻辑,选择在App.razor的OnInitializedAsync方法中编写代码,但遇到了JavaScript互操作相关的错误。

现有代码

@code {
    protected override async Task OnInitializedAsync()
    {
        UserDetails details;
        var user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
        var userId = user.Identity?.Name?.ToLower();
        var result = await BrowserStorage.GetAsync<UserDetails>(userId);

        if (result.Success)
        {
            details = (UserDetails)result.Value;
        }
        else
        {
            GetUserDetailsQuery query = new GetUserDetailsQuery();
            query.UserLogin = await getUserId();
            details = query.Handle();
            await BrowserStorage.SetAsync(userId, details);
        }   
    }
}

遇到的错误

InvalidOperationException: JavaScript interop calls cannot be issued at this time.


解决办法及最佳实践

1. 错误原因

服务器端Blazor中,OnInitializedAsync的执行时机过早,此时JavaScript运行时还未完成初始化,无法调用BrowserStorage这类依赖JS互操作的API。

2. 可行替代方案

方案一:改用OnAfterRenderAsync并控制首次执行

将逻辑移至OnAfterRenderAsync方法,仅在首次渲染时执行,确保JS运行时已准备就绪:

private bool _isInitialized = false;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender && !_isInitialized)
    {
        _isInitialized = true;
        UserDetails details;
        var user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
        var userId = user.Identity?.Name?.ToLower();
        
        var result = await BrowserStorage.GetAsync<UserDetails>(userId);

        if (result.Success)
        {
            details = (UserDetails)result.Value;
        }
        else
        {
            GetUserDetailsQuery query = new GetUserDetailsQuery();
            query.UserLogin = await getUserId();
            details = query.Handle();
            await BrowserStorage.SetAsync(userId, details);
        }   
    }
}

方案二:使用服务器端会话存储(更贴近MVC原有逻辑)

既然是服务器端Blazor,可直接使用ASP.NET Core的ISession存储用户信息,完全规避JS互操作问题:

  1. 在Program.cs中启用会话:
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromHours(1);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 注意中间件顺序,添加在UseRouting之后
app.UseSession();
  1. 在App.razor中注入IHttpContextAccessor访问会话:
@inject IHttpContextAccessor HttpContextAccessor

@code {
    protected override async Task OnInitializedAsync()
    {
        var user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
        var userId = user.Identity?.Name?.ToLower();
        var session = HttpContextAccessor.HttpContext?.Session;
        
        if (session != null && userId != null)
        {
            UserDetails details;
            var storedDetails = session.GetString(userId);
            if (!string.IsNullOrEmpty(storedDetails))
            {
                details = JsonSerializer.Deserialize<UserDetails>(storedDetails);
            }
            else
            {
                GetUserDetailsQuery query = new GetUserDetailsQuery();
                query.UserLogin = await getUserId();
                details = query.Handle();
                session.SetString(userId, JsonSerializer.Serialize(details));
            }
        }
    }
}

方案三:使用全局状态容器

创建全局用户状态服务,在用户首次认证完成后加载一次信息,后续组件直接从状态容器获取:

  1. 定义状态服务:
public class UserStateService
{
    public UserDetails CurrentUser { get; set; }
    public bool IsLoaded { get; set; }
}
  1. 在Program.cs中注册为单例:
builder.Services.AddSingleton<UserStateService>();
  1. 在App.razor中注入并初始化:
@inject UserStateService UserState
@inject AuthenticationStateProvider AuthStateProvider

@code {
    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        var userId = user.Identity?.Name?.ToLower();
        
        if (!UserState.IsLoaded && userId != null)
        {
            GetUserDetailsQuery query = new GetUserDetailsQuery();
            query.UserLogin = await getUserId();
            UserState.CurrentUser = query.Handle();
            UserState.IsLoaded = true;
        }
    }
}

后续任意组件只需注入UserStateService即可直接获取用户信息,无需重复查询。

3. 是否需要每次页面加载查询数据库?

不需要。只要用户会话有效,无论是使用浏览器存储、服务器会话还是全局状态容器,都可以仅在首次加载时查询一次数据库,后续直接从存储/状态中获取,和你之前MVC的逻辑保持一致。

内容的提问来源于stack exchange,提问作者MojoDK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:13:14