Blazor Server重写DbContext SaveChangesAsync获取用户名遇预渲染错误
问题原因与解决方案
问题原因
报错的核心是Blazor Server默认启用的预渲染(Prerendering)机制:
- 预渲染阶段,服务器端尚未与浏览器建立SignalR连接,无法执行JavaScript互操作(你使用的
Blazored.SessionStorage依赖浏览器端存储,必须通过JS调用才能访问)。 - 你的
CustomAuthStateProvider在GetAuthenticationStateAsync中直接调用ISessionStorageService,当DbContext在预渲染阶段触发SaveChangesAsync时,会间接触发这个JS互操作,导致抛出异常。 - 虽然DbContext属于DAL项目,但调用它的Blazor组件处于预渲染生命周期中,整个请求上下文受预渲染限制。
解决方案
方案1:架构解耦(推荐)
DbContext作为数据访问层,不应依赖Blazor的AuthenticationStateProvider,直接从调用方传递审计所需的用户信息:
- 修改DbContext的
SaveChangesAsync方法,接收用户名参数:
public async Task<int> SaveChangesAsync(string userName, CancellationToken cancellationToken = default) { // 在SetAudit中使用传入的userName设置审计字段 SetAudit(userName); return await base.SaveChangesAsync(cancellationToken); }
- 在Blazor组件/业务层中,先获取认证状态,再调用修改后的Save方法:
@inject AuthenticationStateProvider AuthStateProvider @inject YourDbContext DbContext private async Task SubmitData() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var userName = authState.User.Identity.Name ?? "Anonymous"; await DbContext.SaveChangesAsync(userName); }
方案2:让认证提供者兼容预渲染
如果必须在DbContext中获取用户信息,可以修改CustomAuthStateProvider,在预渲染阶段返回匿名身份,避免触发JS互操作:
- 注入
IHttpContextAccessor用于判断预渲染状态:
public class CustomAuthStateProvider : AuthenticationStateProvider { private readonly ISessionStorageService _sessionStorageService; private readonly IHttpContextAccessor _httpContextAccessor; public CustomAuthStateProvider(ISessionStorageService sessionStorageService, IHttpContextAccessor httpContextAccessor) { _sessionStorageService = sessionStorageService; _httpContextAccessor = httpContextAccessor; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { // 检查是否处于预渲染阶段 var isPrerendering = _httpContextAccessor.HttpContext?.Items.ContainsKey("__Prerendering") == true; if (isPrerendering) { // 预渲染时返回匿名身份,避免JS互操作 return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } string authToken = await _sessionStorageService.GetItemAsStringAsync("authToken"); var identity = new ClaimsIdentity(); if (!string.IsNullOrEmpty(authToken)) { try { identity = new ClaimsIdentity(ParseClaimsFromJwt(authToken), "jwt"); } catch { await _sessionStorageService.RemoveItemAsync("authToken"); identity = new ClaimsIdentity(); } } var user = new ClaimsPrincipal(identity); var state = new AuthenticationState(user); NotifyAuthenticationStateChanged(Task.FromResult(state)); return state; } // 保留原有的ParseBase64WithoutPadding和ParseClaimsFromJwt方法 }
- 在Program.cs中注册
IHttpContextAccessor:
builder.Services.AddHttpContextAccessor();
方案3:关闭预渲染(临时应急)
修改_Host.cshtml中的渲染模式,从ServerPrerendered改为Server,彻底禁用预渲染:
<component type="typeof(App)" render-mode="Server" />
注意:此方案会降低首屏加载速度,仅作为临时解决方案,不推荐长期使用。
内容的提问来源于stack exchange,提问作者MDC
相关产品推荐
相关产品推荐

