表单与验证
Blazor的EditForm组件不仅负责生成HTML<form>,还会把表单模型、字段修改状态和验证结果组织到一个EditContext中。它既可用于交互式组件,也可用于.NET 8的静态SSR表单。这篇笔记以学生录入表单为例介绍两种用法。
EditForm和输入组件
下面的交互式表单使用InputText和InputNumber绑定模型属性,并在验证通过后调用保存逻辑。
Components/Pages/StudentCreate.razor
@page "/students/create"
@rendermode InteractiveServer
@inject StudentService StudentService
<EditForm Model="Model" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label for="name">姓名</label>
<InputText id="name" @bind-Value="Model.Name" />
<ValidationMessage For="() => Model.Name" />
</div>
<div>
<label for="age">年龄</label>
<InputNumber id="age" @bind-Value="Model.Age" />
<ValidationMessage For="() => Model.Age" />
</div>
<button type="submit">保存</button>
</EditForm>
@code {
private StudentInput Model { get; set; } = new();
private async Task HandleValidSubmit()
{
await StudentService.CreateAsync(Model);
}
}
Model属性指定表单编辑的模型对象。Blazor提供了一组与EditForm集成的输入组件,常用的包括:
InputText、InputTextArea:文本输入。InputNumber<TValue>:数值输入。InputCheckbox:布尔值输入。InputDate<TValue>:日期输入。InputSelect<TValue>、InputRadioGroup<TValue>:选择和单选输入。
这些组件使用@bind-Value绑定模型属性,而不是普通HTML元素常用的@bind。输入组件会在值发生变化时通知EditContext,使表单能够跟踪哪些字段被修改、哪些字段存在验证错误。静态SSR没有实时交互事件,因此页面上的绑定主要用于输出初始值和生成提交字段,实际的新值会在表单POST后由模型绑定填入。
EditForm还支持OnSubmit和OnInvalidSubmit。OnValidSubmit只在验证成功时执行,OnInvalidSubmit只在验证失败时执行;OnSubmit则不区分验证结果,适合需要自行控制验证流程的场景,这几个提交回调不应混用为同一套重复保存逻辑。
DataAnnotations验证
模型可以使用System.ComponentModel.DataAnnotations中的验证特性声明约束。
using System.ComponentModel.DataAnnotations;
public class StudentInput
{
[Required(ErrorMessage = "请输入姓名")]
[StringLength(20, ErrorMessage = "姓名不能超过20个字符")]
public string Name { get; set; } = string.Empty;
[Range(1, 150, ErrorMessage = "年龄必须在1到150之间")]
public int Age { get; set; }
}
只有在EditForm中加入<DataAnnotationsValidator />后,这些特性才会参与Blazor表单验证。<ValidationSummary />集中显示整个表单的错误,<ValidationMessage For="..." />显示指定字段的错误,两者可以按需选择或组合使用。客户端或交互式UI中的验证只能改善体验,服务器保存数据前仍应执行必要的业务验证和授权检查。
直接使用EditContext
EditContext是表单状态和验证状态的核心对象。除了让EditForm根据Model自动创建它,我们也可以自己创建并传入:
<EditForm EditContext="editContext" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<InputText @bind-Value="Model.Name" />
<button type="submit">保存</button>
</EditForm>
@code {
private StudentInput Model { get; set; } = new();
private EditContext editContext = default!;
protected override void OnInitialized()
{
editContext = new EditContext(Model);
}
private void HandleValidSubmit()
{
// 保存Model。
}
}
需要动态切换编辑对象、订阅OnFieldChanged或OnValidationStateChanged、手动调用Validate()时,通常会直接操作EditContext。注意EditForm的Model和EditContext参数不能同时设置,否则会在运行时抛出异常。如果订阅了EditContext事件,组件释放时还应取消订阅,避免保留不必要的引用。
静态SSR表单
.NET 8为静态SSR表单引入了FormName和[SupplyParameterFromForm]。静态SSR中没有交互事件,浏览器提交的是普通HTTP表单POST,服务器收到请求后再绑定模型、执行验证和提交回调。
@page "/students/create-static"
@inject StudentService StudentService
<EditForm Model="Model"
FormName="CreateStudent"
Enhance
OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<label for="name">姓名</label>
<InputText id="name" @bind-Value="Model!.Name" />
<ValidationMessage For="() => Model!.Name" />
<button type="submit">保存</button>
</EditForm>
@code {
[SupplyParameterFromForm]
private StudentInput? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new StudentInput();
}
private async Task HandleValidSubmit()
{
await StudentService.CreateAsync(Model!);
}
}
这里有几点需要注意:
FormName为表单提供明确且唯一的名称,静态SSR中应为每个表单设置它;一个页面放置多个表单时尤其不能重名。[SupplyParameterFromForm]指示Blazor从提交的表单值创建或填充Model。OnInitialized()中的Model ??= new()同时兼顾首次GET显示空表单和POST绑定已有值。Enhance启用增强表单提交,在脚本可用时减少整页刷新;没有启用增强导航时,表单仍可通过普通HTTP POST工作。它不会把静态SSR组件变成交互式组件。- 静态SSR表单依赖防伪令牌防止跨站请求伪造。
EditForm会处理表单令牌,但应用管道必须调用app.UseAntiforgery(),并确保该中间件在映射Razor组件端点前生效;如果应用使用身份验证和授权中间件,通常将UseAntiforgery()放在它们之后。
Program.cs中的关键顺序如下:
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
如果未使用身份验证,可以省略前两行,但不能因为表单可以提交就忽略防伪保护。静态SSR表单的每次提交都是新的服务器处理过程,不要依赖上一次请求中普通组件字段仍然保留。