.NET 9 Blazor Auto项目登录页输入内容丢失问题排查
Blazor Auto模式登录页面输入内容丢失问题的成因与解决方法
问题成因
水化过程的状态覆盖
Blazor Auto模式会先完成服务器端静态渲染,再由客户端JavaScript接管完成水化。如果用户在水化完成前(约1秒内)输入内容,客户端水化完成后会用服务器端初始渲染的空白状态覆盖用户输入,导致内容丢失。SupplyParameterFromForm属性误用
你的LoginModel被标记了[SupplyParameterFromForm],该属性用于从表单POST请求中绑定参数,但登录页面是用户主动输入场景,并非表单回传,这个属性会触发水化时框架重新绑定Model,重置用户输入。初始化时机冲突
在构造函数中初始化Model和EditContext,而不是使用Blazor组件生命周期方法。构造函数会在服务器端渲染和客户端水化时各执行一次,重复初始化会覆盖用户已输入的内容。单向绑定的同步缺陷
DxTextBox使用单向绑定Text="@Model.Email"配合TextExpression,这种方式无法在水化过程中实时同步用户输入到Model,页面重新渲染时会丢失数据。
解决方法
1. 移除SupplyParameterFromForm属性
登录页面不需要从表单参数绑定Model,直接删除该属性:
// 移除此行 // [SupplyParameterFromForm] public LoginModel Model { get; set; } = new LoginModel();
2. 调整初始化逻辑到组件生命周期
将Model和EditContext的初始化放到OnInitialized中,避免构造函数重复执行:
public LoginModel Model { get; set; } = new LoginModel(); public EditContext EditContext { get; set; } public ValidationMessageStore ValidationMessageStore { get; set; } protected override void OnInitialized() { EditContext = new EditContext(Model); ValidationMessageStore = new ValidationMessageStore(EditContext); }
3. 使用双向绑定替代单向绑定
将DxTextBox的Text和TextExpression替换为双向绑定@bind-Text,确保输入实时同步到Model:
<DxTextBox name="Model.Email" CssClass="mt-1" autofocus @bind-Text="@Model.Email" ShowValidationIcon="true" />
<DxTextBox name="Model.Password" CssClass="mt-1" @bind-Text="@Model.Password" ShowValidationIcon="true" Password="true" />
4. 可选:跳过静态渲染(按需使用)
如果不需要静态渲染,可以在页面顶部明确指定交互式渲染模式,避免水化过程:
@page "/Authentication/Login" @rendermode InteractiveAuto
修改后的关键代码示例
@page "/Authentication/Login" @inject IUserService UserService @inject NavigationManager NavigationManager <PageTitle>Login</PageTitle> <EditForm class="box-border flex flex-grow flex-col items-center justify-center p-8" FormName="LoginForm" Model="Model" OnInvalidSubmit="HandleInvalidSubmit" OnValidSubmit="HandleValidSubmitAsync"> <DataAnnotationsValidator /> <div class="box-border flex w-full max-w-xs flex-col p-8 shadow"> <h1 class="text-lg">Login</h1> <div class="mt-4 flex flex-col"> <label class="text-xs opacity-75">Email</label> <DxTextBox name="Model.Email" CssClass="mt-1" autofocus @bind-Text="@Model.Email" ShowValidationIcon="true" /> </div> <div class="mt-2 flex flex-col"> <label class="text-xs opacity-75">Password</label> <DxTextBox name="Model.Password" CssClass="mt-1" @bind-Text="@Model.Password" ShowValidationIcon="true" Password="true" /> </div> <!-- 其余UI部分保持不变 --> </div> </EditForm> @code { [Parameter] [SupplyParameterFromQuery] public string RedirectUri { get; set; } = string.Empty; [Parameter] [SupplyParameterFromQuery] public string Code { get; set; } = string.Empty; [Parameter] [SupplyParameterFromQuery(Name = "RequestedScopes")] public string RequestedScopesAsString { get; set; } = string.Empty; [Parameter] [SupplyParameterFromQuery] public string Nonce { get; set; } = string.Empty; [Parameter] [SupplyParameterFromQuery] public string State { get; set; } = string.Empty; public List<string> RequestedScopes => RequestedScopesAsString.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(); public EditContext EditContext { get; set; } public ValidationMessageStore ValidationMessageStore { get; set; } public LoginModel Model { get; set; } = new LoginModel(); protected override void OnInitialized() { EditContext = new EditContext(Model); ValidationMessageStore = new ValidationMessageStore(EditContext); } // 其余方法保持不变 }
内容的提问来源于stack exchange,提问作者Slamdunk
相关产品推荐
相关产品推荐

