ASP.NET Core 中的模型绑定

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET Core 中的模型绑定相关的知识,希望对你有一定的参考价值。

参考技术A

控制器 Razor 和页面处理来自 HTTP 请求的数据。 例如,路由数据可以提供一个记录键,而发布的表单域可以为模型的属性提供一个值。 编写代码以检索这些值,并将其从字符串转换为 .NET 类型不仅繁琐,而且还容易出错。 模型绑定后会自动化该过程。 模型绑定系统:

假设有以下操作方法:

并且应用收到一个带有以下消息 URL 的请求:

在路由系统选择该操作方法之后,模型绑定执行以下步骤:

然后,该框架会调用 GetById 方法,为 id 参数传入 2,并为 dogsOnly 参数传入 true。

在前面的示例中,模型绑定目标是简单类型的方法参数。 目标也可以是复杂类型的属性。 成功绑定每个属性后,将对属性进行模型验证。 有关绑定到模型的数据以及任意绑定或验证错误的记录都存储在 ControllerBase.ModelState 或 PageModel.ModelState 中。 为查明该过程是否已成功,应用会检查 ModelState.IsValid 标志。

模型绑定尝试查找以下类型目标的值:

可应用于控制器或 PageModel 类的公共属性,从而使模型绑定以该属性为目标:


可在 ASP.NET Core 2.1 及更高版本中获得。 可应用于控制器或 PageModel 类,以使模型绑定以该类的所有公共属性为目标:


默认情况下,不绑定 HTTP GET 请求的属性。 通常,GET 请求只需一个记录 ID 参数。 记录 ID 用于查找数据库中的数据。 因此,无需绑定包含模型实例的属性。 只需要将属性绑定到 GET 请求中的数据的情况下,请将 SupportsGet 属性设置为 true:


默认情况下,模型绑定以键值对的形式出现 HTTP 请求从以下源中获取数据:

对于每个目标参数或属性,按照之前列表中指示的顺序扫描清楚。 有几个例外情况:

如果默认源不正确,请使用下列属性之一来指定源:

这些属性:


将 [FromBody] 特性应用于一个参数,以便从一个 HTTP 请求的正文填充其属性。 ASP.NET Core 运行时将读取正文的责任委托给输入格式化程序。 输入格式化程序的解释位于本文后面部分。

将 [FromBody] 应用于复杂类型参数时,应用于其属性的任何绑定源属性都将被忽略。 例如,以下 Create 操作指定从正文填充其 pet 参数:


Pet 类指定从查询字符串参数填充其 Breed 属性:


在上面的示例中:

输入格式化程序只读取正文,不了解绑定源特性。 如果在正文中找到合适的值,则使用该值填充 Breed 属性。

不要将 [FromBody] 应用于每个操作方法的多个参数。 输入格式化程序读取请求流后,无法再次读取该流以绑定其他 [FromBody] 参数。

源数据由“值提供程序”提供给模型绑定系统。 你可以编写并注册自定义值提供程序,这些提供程序从其他源中获取用于模型绑定的数据。 例如,你可能希望数据来自 cookie 或 会话状态。 要从新的源中获取数据,请执行以下操作:

示例应用包括从获取值的值提供程序和工厂 cookie 示例。 以下是 Startup.ConfigureServices 中的注册代码:


所示代码将自定义值提供程序置于所有内置值提供程序之后。 要将其置于列表中的首位,请调用 Insert(0, new CookieValueProviderFactory()) 而不是 Add。

默认情况下,如果找不到模型属性的值,则不会创建模型状态错误。 该属性设置为 NULL 或默认值:

如果在模型属性的窗体字段中未找到任何内容时模型状态应失效,请使用 [BindRequired] 特性。

请注意,此 [BindRequired] 行为适用于发布的表单数据中的模型绑定,而不适用于请求正文中的 JSON 或 XML 数据。 请求正文数据由输入格式化程序进行处理。

如果找到源,但无法将其转换为目标类型,则模型状态将被标记为无效。 目标参数或属性设置为 NULL 或默认值,如上一部分所述。

在具有 [ApiController] 属性的 API 控制器中,无效的模型状态会导致自动 HTTP 400 响应。

在 Razor 页面中,重新显示页面并显示一条错误消息:


客户端验证将捕获大多数提交到 Razor 页面窗体的错误数据。 此验证使得先前突出显示的代码难以被触发。 示例应用包含一个“提交无效日期”按钮,该按钮将错误数据置于“雇用日期”字段中并提交表单。 此按钮显示在发生数据转换错误时用于重新显示页的代码将如何工作。

在使用先前的代码重新显示页时,表单域中不会显示无效的输入。 这是因为模型属性已设置为 NULL 或默认值。 无效输入会出现在错误消息中。 但是,如果要在表单域中重新显示错误数据,可以考虑将模型属性设置为字符串并手动执行数据转换。

如果不希望发生类型转换错误导致模型状态错误的情况,建议使用相同的策略。 在这种情况下,将模型属性设置为字符串。

模型绑定器可以将源字符串转换为以下简单类型:

复杂类型必须具有要绑定的公共默认构造函数和公共可写属性。 进行模型绑定时,将使用公共默认构造函数来实例化类。

对于复杂类型的每个属性,模型绑定会查找名称模式 prefix.property_name 的源。 如果未找到,它将仅查找不含前缀的 properties_name。

对于绑定到参数,前缀是参数名称。 对于绑定到 PageModel 公共属性,前缀是公共属性名称。 某些属性具有 Prefix 属性,让你可以替代参数或属性名称的默认用法。

例如,假设复杂类型是以下 Instructor 类:


如果要绑定的模型是一个名为 instructorToUpdate 的参数:


模型绑定从查找键 instructorToUpdate.ID 的源开始操作。 如果未找到,它将查找不含前缀的 ID。

如果要绑定的模型是控制器或 PageModel 类的一个名为 Instructor 的属性:


模型绑定从查找键 Instructor.ID 的源开始操作。 如果未找到,它将查找不含前缀的 ID。

如果要绑定的模型是名为 instructorToUpdate 的参数,并且 Bind 属性指定 Instructor 作为前缀:


模型绑定从查找键 Instructor.ID 的源开始操作。 如果未找到,它将查找不含前缀的 ID。

多个内置属性可用于控制复杂类型的模型绑定:

警告

如果发布的表单数据是值的源,则这些属性会影响模型绑定。 它们 影响输入格式化程序,后者处理已发布的 JSON 和 XML 请求正文。 输入格式化程序的解释位于本文后面部分。

可应用于类或方法参数。 指定模型绑定中应包含的模型属性。 [Bind] 不 影响输入 格式化程序。

在下面的示例中,当调用任意处理程序或操作方法时,只绑定 Instructor 模型的指定属性:


在下面的示例中,当调用 OnPost 方法时,只绑定 Instructor 模型的指定属性:


[Bind] 属性可用于防止“创建”方案中的过多发布情况。 由于排除的属性设置为 NULL 或默认值,而不是保持不变,因此它在编辑方案中无法很好地工作。 为防止过多发布,建议使用视图模型,而不是使用 [Bind] 属性。 有关详细信息,请参阅有关过多发布的安全性说明。

ModelBinderAttribute 可应用于类型、属性或参数。 它允许指定用于绑定特定实例或类型的模型绑定器的类型。 例如:


[ModelBinder]当属性或参数处于模型绑定时,该属性还可用于更改属性或参数的名称:


只能应用于模型属性,不能应用于方法参数。 如果无法对模型属性进行绑定,则会导致模型绑定添加模型状态错误。 下面是一个示例:


另请参阅模型验证中针对 [Required] 属性的讨论。

只能应用于模型属性,不能应用于方法参数。 防止模型绑定设置模型的属性。 下面是一个示例:


对于是简单类型集合的目标,模型绑定将查找 parameter_name 或 property_name 的匹配项。 如果找不到匹配项,它将查找某种不含前缀的受支持的格式。 例如:

对于 Dictionary 目标,模型绑定会查找 parameter_name 或 property_name 的匹配项。 如果找不到匹配项,它将查找某种不含前缀的受支持的格式。 例如:

模型绑定要求复杂类型具有无参数的构造函数。 System.Text.Json和 Newtonsoft.Json 基于的输入格式化程序都支持对不具有无参数构造函数的类进行反序列化。

C # 9 引入了记录类型,这是一个很好的方法,可以在网络上简洁地表示数据。 ASP.NET Core 增加了对模型绑定和使用单个构造函数验证记录类型的支持:


Person/Index.cshtml:


在验证记录类型时,运行时将搜索专用于参数而不是属性的绑定和验证元数据。

该框架允许绑定到记录类型并对其进行验证:


若要运行上述操作,类型必须:

不能绑定没有无参数构造函数的 Poco。

下面的代码会引发异常,指出类型必须具有无参数的构造函数:


具有类似于主构造函数的手动创作构造函数的记录类型工作


对于记录类型,使用参数上的验证和绑定元数据。 忽略属性上的任何元数据


验证使用参数的元数据,但使用属性来读取值。 在带有主构造函数的普通情况下,两者都是相同的。 不过,有几种方法可以实现此操作:



在这种情况下,MVC 不会尝试 Name 重新绑定。 但 Age 允许更新

ASP.NET Core 路由值提供程序和查询字符串值提供程序:

相反,来自窗体数据的值要进行区分区域性的转换。 这是设计使然,目的是让 URL 可在各个区域设置中共享。

使 ASP.NET Core 路由值提供程序和查询字符串值提供程序进行区分区域性的转换:



模型绑定可以处理某些特殊的数据类型。

HTTP 请求中包含的上传文件。 还支持多个文件的 IEnumerable 。

操作可以选择将 CancellationToken 作为参数绑定。 这会将信号绑定到 RequestAborted HTTP 请求的基础的连接中止时。 操作可以使用此参数来取消长时间运行的异步操作,这些操作将作为控制器操作的一部分执行。

用于从发布的表单数据中检索所有的值。

请求正文中的数据可以是 JSON、XML 或其他某种格式。 要分析此数据,模型绑定会使用配置为处理特定内容类型的输入格式化程序。 默认情况下,ASP.NET Core 包括用于处理 JSON 数据的基于 JSON 的输入格式化程序。 可以为其他内容类型添加其他格式化程序。

ASP.NET Core 基于 Consumes 属性来选择输入格式化程序。 如果没有属性,它将使用 Content-Type 标头。

要使用内置 XML 输入格式化程序,请执行以下操作:

由输入格式化程序完全负责从请求正文读取数据。 若要自定义此过程,请配置输入格式化程序使用的 API。 此部分介绍如何自定义基于 System.Text.Json 的输入格式化程序,以了解自定义类型 ObjectId。

以包含自定义 ObjectId 属性 Id 的模型为例:


使用 System.Text.Json 时,若要自定义模型绑定过程,请创建派生自 JsonConverter 的类:


将 JsonConverterAttribute 属性应用到此类型,以使用自定义转换器。 在下面的示例中,为 ObjectId 类型配置了 ObjectIdConverter 来作为其自定义转换器:


有关详细信息,请参阅如何编写自定义转换器。

模型绑定和验证系统的行为由 ModelMetadata 驱动。 可通过向 MvcOptions.ModelMetadataDetailsProviders 添加详细信息提供程序来自定义 ModelMetadata。 内置详细信息提供程序可用于禁用指定类型的模型绑定或验证。

要禁用指定类型的所有模型的模型绑定,请在 Startup.ConfigureServices 中添加 ExcludeBindingMetadataProvider。 例如,禁用对 System.Version 类型的所有模型的模型绑定:


要禁用指定类型的属性的验证,请在 Startup.ConfigureServices 中添加 SuppressChildValidationMetadataProvider。 例如,禁用对 System.Guid 类型的属性的验证:


通过编写自定义模型绑定器,并使用 [ModelBinder] 属性为给定目标选择该模型绑定器,可扩展模型绑定。 详细了解自定义模型绑定。

可以使用 TryUpdateModelAsync 方法手动调用模型绑定。 ControllerBase 和 PageModel 类上均定义了此方法。 方法重载允许指定要使用的前缀和值提供程序。 如果模型绑定失败,该方法返回 false。 下面是一个示例:


TryUpdateModelAsync 使用值提供程序从窗体正文、查询字符串和路由数据获取数据。 TryUpdateModelAsync 通常有以下特点:

有关详细信息,请参阅 TryUpdateModelAsync。

此属性的名称遵循指定数据源的模型绑定属性的模式。 但这与绑定来自值提供程序的数据无关。 它从依赖关系注入容器中获取类型的实例。 其目的在于,在仅当调用特定方法时需要服务的情况下,提供构造函数注入的替代方法。

以上是关于ASP.NET Core 中的模型绑定的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 中的模型绑定

模型绑定不适用于 ASP.NET Core 2 WebAPI 中的 POST 请求

ASP.NET Core 中的模型绑定将下划线映射到标题大小写属性名称

理解ASP.NET Core

ASP.NET Core 1 Web API 模型绑定数组

在 ASP.NET MVC Core 2 中使用 MetadataPropertyHandling 模型绑定 JSON 数据