注意
此版本不是本文的最新版本。 有关当前版本,请参阅 本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅 本文的 .NET 9 版本。
本文介绍如何使用 Blazor 中的窗体。
输入组件和表单
Blazor 框架支持窗体并提供内置输入组件:
注意
不支持 ASP.NET 核心验证功能包含在 “不支持的验证功能 ”部分中。
Microsoft.AspNetCore.Components.Forms 命名空间提供以下内容:
- 用于管理窗体元素、状态和验证的类。
- 访问内置 Input* 组件。
从 Blazor 项目模板创建的项目在应用的 _Imports.razor
文件中包含命名空间,这使命名空间对应用的 Razor 组件可用。
支持标准 HTML 窗体。 使用常规 HTML <form>
标签创建表单,并指定一个 @onsubmit
处理程序来处理提交表单请求。
StarshipPlainForm.razor
:
@page "/starship-plain-form"
@inject ILogger<StarshipPlainForm> Logger
<form method="post" @onsubmit="Submit" @formname="starship-plain-form">
<AntiforgeryToken />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
public string? Id { get; set; }
}
}
@page "/starship-plain-form"
@inject ILogger<StarshipPlainForm> Logger
<form method="post" @onsubmit="Submit" @formname="starship-plain-form">
<AntiforgeryToken />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
public string? Id { get; set; }
}
}
在上述的 StarshipPlainForm
组件中:
- 表单呈现在
<form>
元素的显示位置。 该窗体使用@formname
指令属性命名,这会将该窗体唯一标识到 Blazor 框架。 - 模型在组件的
@code
块中创建,并保存在公共属性 (Model
) 中。[SupplyParameterFromForm]
属性指示应从表单数据中提供关联属性的值。 与属性名称匹配的请求中的数据绑定到该属性。 -
InputText 组件是用于编辑字符串值的输入组件。
@bind-Value
指令属性将Model.Id
模型属性绑定到 InputText 组件的 Value 属性。 -
Submit
方法被注册为@onsubmit
回调的处理程序。 当用户提交表单时,该处理程序将被调用。
重要
始终使用具有唯一窗体名称的 @formname
指令属性。
Blazor 通过截获请求将响应应用到现有 DOM,从而尽可能保留呈现的窗体,以增强页面导航和窗体处理。 增强功能可避免完全加载页面,可提供更流畅的用户体验,类似于单页应用 (SPA),尽管组件是在服务器上呈现。 有关详细信息,请参阅 ASP.NET 核心 Blazor 路由和导航。
纯 HTML 表单支持流式呈现。 请注意,在 POST
表单时,只有表单处理程序内部的 DOM 更新才会被流式传输(例如,@onsubmit
)。
OnInitializedAsync
内的更新仅针对 GET
请求进行流式传输。 有关详细信息,请参阅“允许流式传输 POST 响应的加载阶段”(dotnet/aspnetcore
#50994)。
注意
指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET 核心源代码的版本标记(dotnet/AspNetCore.Docs #26205)。
前面的示例在表单中包含AntiforgeryToken组件,以包含防伪支持。 本文的防伪支持部分会进一步介绍防伪支持。
若要根据其他元素的 DOM 事件(例如 oninput
或 onblur
)提交表单,请使用 JavaScript 提交表单(submit
)。
使用框架的Blazor组件,通常使用Blazor的内置表单支持以定义表单,而不是在EditForm应用中使用纯表单。 以下 Razor 组件演示了使用 Razor 组件来呈现 WebForm 的典型元素、组件和 EditForm 代码。
Starship1.razor
:
@page "/starship-1"
@inject ILogger<Starship1> Logger
<EditForm Model="Model" OnSubmit="Submit" FormName="Starship1">
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
public string? Id { get; set; }
}
}
@page "/starship-1"
@inject ILogger<Starship1> Logger
<EditForm Model="Model" OnSubmit="Submit" FormName="Starship1">
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
public string? Id { get; set; }
}
}
在上述的 Starship1
组件中:
-
EditForm 组件会呈现在
<EditForm>
元素出现的位置。 该窗体使用 FormName 属性进行命名,该属性唯一地标识了 Blazor 框架的窗体。 - 模型在组件的
@code
块中创建,并保存在公共属性 (Model
) 中。 系统会将该属性分配给 EditForm.Model 参数。[SupplyParameterFromForm]
属性指示应从表单数据中提供关联属性的值。 与属性名称匹配的请求中的数据绑定到该属性。 - 该 InputText 组件是用于编辑字符串值的 输入组件 。
@bind-Value
指令属性将Model.Id
模型属性绑定到 InputText 组件的 Value 属性。 -
Submit
方法注册为 OnSubmit 回调的处理程序。 当用户提交表单时,该处理程序将被调用。
重要
始终使用具有唯一窗体名称的 FormName 属性。
Blazor增强了EditForm组件的页面导航和表单处理。 有关详细信息,请参阅 ASP.NET 核心 Blazor 路由和导航。
支持EditForm。 请注意,在 POST
表单时,只有表单处理程序内部的 DOM 更新才会被流式传输(例如,OnValidSubmit
)。
OnInitializedAsync
内的更新仅针对 GET
请求进行流式传输。 有关详细信息,请参阅“允许流式传输 POST 响应的加载阶段”(dotnet/aspnetcore
#50994)。
注意
指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET 核心源代码的版本标记(dotnet/AspNetCore.Docs #26205)。
@page "/starship-1"
@inject ILogger<Starship1> Logger
<EditForm Model="Model" OnSubmit="Submit">
<InputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
@code {
public Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit()
{
Logger.LogInformation("Model.Id = {Id}", Model?.Id);
}
public class Starship
{
public string? Id { get; set; }
}
}
在上述的 Starship1
组件中:
†有关属性绑定的详细信息,请参阅 ASP.NET 核心 Blazor 数据绑定。
在下一个示例中,修改上述组件以在 Starship2
组件中创建窗体:
- OnSubmit 已替换为 OnValidSubmit,如果窗体在用户提交时有效,则该组件处理分配的事件处理程序。
- 添加了一个 ValidationSummary 组件,如果在提交窗体时窗体无效,则会显示验证消息。
- 数据注释验证程序(DataAnnotationsValidator 组件†)使用数据注释附加验证支持:
- 如果在选择
<input>
按钮时Submit
表单域为空,则验证摘要(ValidationSummary 组件‡)(“The Id field is required.
”)会显示错误,并且系统不会调用Submit
。 - 如果在选择
<input>
按钮时Submit
窗体字段包含超过十个字符,则验证摘要中显示错误 ("Id is too long.
")。Submit
未调用。 - 如果在选择
<input>
按钮时Submit
表单域包含有效的值,则系统会调用Submit
。
- 如果在选择
†DataAnnotationsValidator部分中介绍了该组件。 ‡ValidationSummary部分中涵盖了组件。
Starship2.razor
:
@page "/starship-2"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship2> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship2">
<DataAnnotationsValidator />
<ValidationSummary />
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
<button type="submit">Submit</button>
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-2"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship2> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship2">
<DataAnnotationsValidator />
<ValidationSummary />
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
<button type="submit">Submit</button>
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Id = {Id}", Model?.Id);
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-2"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship2> Logger
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
@code {
public Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit()
{
Logger.LogInformation("Id = {Id}", Model?.Id);
}
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
处理窗体提交
EditForm 提供以下回调来处理窗体提交:
- 使用 OnValidSubmit 来指定一个事件处理程序,使其在提交包含有效字段的表单时运行。
- 使用 OnInvalidSubmit 分配事件处理程序,让其在提交包含无效字段的表单时运行。
- 使用 OnSubmit 分配事件处理程序,让其在不考虑窗体字段验证状态的情况下运行。 通过在事件处理程序方法中调用 EditContext.Validate 来验证表单。 如果 Validate 返回
true
,则窗体有效。
清除表单或字段
通过将其模型重置为默认状态来重置表单,该操作可以在EditForm的标记内部或外部执行。
<button @onclick="ClearForm">Clear form</button>
...
private void ClearForm() => Model = new();
或者,使用显式 Razor 表达式:
<button @onclick="@(() => Model = new())">Clear form</button>
通过将字段的模型值清除回其默认状态来重置字段:
<button @onclick="ResetId">Reset Identifier</button>
...
private void ResetId() => Model!.Id = string.Empty;
或者,使用显式 Razor 表达式:
<button @onclick="@(() => Model!.Id = string.Empty)">Reset Identifier</button>
在前面的示例中不需要调用 StateHasChanged,因为调用事件处理程序后,StateHasChanged 框架会自动调用 Blazor 来重新呈现组件。 如果不使用事件处理程序调用清除窗体或字段的方法,则应使用开发人员代码调用 StateHasChanged 来重新呈现组件。
防伪支持
在 Blazor 文件中调用 AddRazorComponents 时,会自动将防伪服务添加到 Program
应用。
应用通过在 UseAntiforgery 文件中的处理管道请求中调用 Program
来使用防伪中间件。
UseAntiforgery 在调用 UseRouting 之后调用。 如果有对 UseRouting 和 UseEndpoints 的调用,则对 UseAntiforgery 的调用必须介于两者之间。 对 UseAntiforgery 的调用必须在对 UseAuthentication 和 UseAuthorization 的调用后发出。
AntiforgeryToken 组件将防伪标记呈现为隐藏字段,[RequireAntiforgeryToken]
属性启用防伪保护。 如果防伪检查失败,则会引发 400 - Bad Request
响应,并且不会处理表单。
对于基于 EditForm 的窗体,会自动添加 AntiforgeryToken 组件和 [RequireAntiforgeryToken]
特性以提供防伪保护。
对于基于 HTML <form>
元素的表单,请手动将AntiforgeryToken组件添加到表单:
<form method="post" @onsubmit="Submit" @formname="starshipForm">
<AntiforgeryToken />
<input id="send" type="submit" value="Send" />
</form>
@if (submitted)
{
<p>Form submitted!</p>
}
@code{
private bool submitted = false;
private void Submit() => submitted = true;
}
警告
对于基于 EditForm 或 HTML <form>
元素的窗体,可以通过将 required: false
传递给 [RequireAntiforgeryToken]
属性来禁用防伪保护。 以下示例禁用防伪造功能,不建议 适用于公共应用:
@using Microsoft.AspNetCore.Antiforgery
@attribute [RequireAntiforgeryToken(required: false)]
有关详细信息,请参阅 ASP.NET 核心 Blazor 身份验证和授权。
缓解过度发布攻击
静态呈现的服务器端表单(例如通常用于使用表单模型在数据库中创建和编辑记录的组件中的表单)可能容易受到过度发布攻击(也称为批量分配攻击)。 当恶意用户向服务器发出 HTML 表单 POST 请求,而服务器处理了不属于呈现表单的属性的数据且开发人员不希望允许用户修改这些数据时,就会发生过度发布攻击。 “过度发布”一词的字面意思是恶意用户在表单上过度发布。
当模型不包含用于创建和更新操作的受限属性时,无需担心过度发布。 但是,在使用维护的基于静态 SSR 的 Blazor 表单时,请务必警惕过度发布。
若要缓解过度发布,建议将针对表单和数据库的单独视图模型/数据传输对象 (DTO) 用于创建(插入)和更新操作。 提交表单时,组件和 C# 代码仅使用视图模型/DTO 的属性来修改数据库。 恶意用户包含的任何额外数据将被丢弃,因此恶意用户无法执行过度发布攻击。
增强型表单处理
通过为Enhance表单提供参数EditForm或为 HTML 表单添加属性data-enhance
来增强表单 POST 请求的导航功能(<form>
):
<EditForm ... Enhance ...>
...
</EditForm>
<form ... data-enhance ...>
...
</form>
不支持: 不能在表单的祖先元素上设置增强导航,以启用增强的表单处理。
<div ... data-enhance ...>
<form ...>
<!-- NOT enhanced -->
</form>
</div>
增强型表单发布仅适用于 Blazor 终结点。 将增强型表单提交到非 Blazor 终结点会导致错误。
禁用增强型表单处理:
- 对于 EditForm,请从表单元素中移除 Enhance 参数(或将其设置为
false
:Enhance="false"
)。 - 对于 HTML
<form>
,请从表单元素中移除data-enhance
特性(或将其设置为false
:data-enhance="false"
)。
Blazor如果更新的内容不是服务器呈现的一部分,则增强的导航和表单处理可能会撤消对 DOM 的动态更改。 若要保留元素的内容,请使用 data-permanent
特性。
在以下示例中,当页面加载时,<div>
元素的内容由脚本动态更新:
<div data-permanent>
...
</div>
若要全局禁用增强的导航和表单处理,请参阅 ASP.NET Core Blazor 启动。
有关如何使用 enhancedload
事件来侦听增强页面更新的指南,请参阅 ASP.NET Core Blazor 路由和导航。
示例
示例不采用增强的表单处理来进行表单 POST 请求,但可以通过遵循增强型表单处理部分中的指导,更新所有示例以采用这些增强功能。
示例使用与 C# 9 和 .NET 5 引入的 目标类型new
运算符。 在以下示例中,new
运算符未显式声明类型:
public ShipDescription ShipDescription { get; set; } = new();
如果使用 C# 8 或更早版本(ASP.NET Core 3.1),请修改示例代码以将类型声明为 new
运算符:
public ShipDescription ShipDescription { get; set; } = new ShipDescription();
组件使用可空引用类型 (NRT),.NET 编译器执行 null 状态静态分析,.NET 6 或更高版本支持两者。 有关详细信息,请参阅 从 .NET 5 中的 ASP.NET Core 迁移到 .NET 6。
如果使用 C# 9 或更早版本(.NET 5 或更早版本),请从示例中删除 NRT。 通常,这只涉及从示例代码的类型中删除问号 (?
) 和感叹号 (!
)。
面向 .NET 6 或更高版本时,.NET SDK 将隐式全局 using
指令应用于项目。 示例使用记录器记录有关窗体处理的信息,但不需要在组件示例中为 @using
命名空间指定 Microsoft.Extensions.Logging 指令。 有关详细信息,请参阅 .NET 项目 SDK:隐式 using 指令。
如果使用 C# 9 或更早版本(.NET 5 或更早版本),请在@page
指令之后,将@using
指令添加到组件顶部,以满足示例所需的任何 API。 通过 Visual Studio 查找 API 命名空间(右键单击对象并选择 “速览定义”或 .NET API 浏览器)。
为了演示如何使用 数据注释 验证表单,示例组件依赖于 System.ComponentModel.DataAnnotations API。 如果希望避免在使用数据注释的组件中使用额外代码行,请在整个应用的组件中使用导入文件(_Imports.razor
)提供命名空间:
@using System.ComponentModel.DataAnnotations
窗体示例引用 Star Trek(星际迷航)世界的各个方面。 星际迷航是 哥伦比亚广播公司工作室 和 派拉蒙的 1966-2023 年版权©。
客户端验证需要电路
在 Blazor Web App 中,客户端验证需要活动 BlazorSignalR 线路。 组件中采用静态服务器端呈现(静态 SSR)的表单无法使用客户端验证。 采用静态 SSR 的表单会在表单提交后在服务器上进行验证。
不支持的验证功能
除 Blazor外,[Remote]
支持所有数据注释内置验证器。
Razor 组件不支持 jQuery 验证。 建议使用以下任一方法:
- 请遵循 ASP.NET CoreBlazor 窗体验证中的指导:
- 采用交互式呈现模式的 Blazor Web App 中的服务器端验证。
- 独立 Blazor Web 程序集应用中的客户端验证。
- 使用本机 HTML 验证属性(请参阅 客户端表单验证)。
- 采用第三方验证 JavaScript 库。
对于服务器上的静态渲染表单,一个用于客户端验证的新机制正在考虑在 2025 年底用于 .NET 10。 有关详细信息,请参阅使用 Blazor 在无电路 (dotnet/aspnetcore
#51040) 的情况下通过客户端验证创建服务器呈现窗体。
其他资源
- ASP.NET 核心 Blazor 文件上传
- 使用 Microsoft Entra ID 保护托管 ASP.NET 核心 Blazor WebAssembly 应用
- 使用 Azure Active Directory B2C 保护托管 ASP.NET 核心 Blazor WebAssembly 应用
- 使用Blazor WebAssembly服务器保护托管 ASP.NET 核心Identity应用
-
Blazor示例 GitHub 存储库 () (
dotnet/blazor-samples
如何下载) -
ASP.NET Core GitHub 存储库 (
dotnet/aspnetcore
) 窗体测试资产