在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
此为系列文章,对MSDN ASP.NET Core 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解。 这篇文章解释了模型绑定是什么,它是如何工作的,以及如何自定义它的行为。 什么是模型绑定 控制器以及Razor 页面与来自于HTTP请求的数据一起工作。举个例子,路由数据或许会提供一个记录键,post 表单字段可能会为模型的属性提供值。如果编写代码来取出这些值并将它们从字符串类型转换为.NET类型将会是枯燥乏味并且是容易出错的。而模型绑定将这个过程自动化。模型绑定系统:
示例 假设你有如下的动作(Action)方法: [HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool dogsOnly) 而且app接受到了一个带有如下URL的请求: http://contoso.com/api/pets/2?DogsOnly=true 在路由系统选择合适的动作方法之后,模型绑定会经理如下的步骤:
框架然后会调用 在之前的示例中,模型参数目标是简单类型的方法参数。模型绑定的目标也可能是复杂类型的属性。在每个属性都被成功绑定后,那个属性会发生model validation。什么数据被绑定给模型的记录,以及任何绑定或者验证错误,都会存储在ControllerBase.ModelState 或 PageModel.ModelState中。为了证实这个过程是否成功,可以检查ModelState.IsValid标记。 目标 模型绑定尝试为如下类型的目标查找数值:
[BindProperty] 属性 这个属性可被应用于一个控制器或者是PageModel 类的 public 属性上,以在那个属性上实现模型绑定。 public class EditModel : InstructorsPageModel { [BindProperty] public Instructor Instructor { get; set; } [BindProperties] 属性 在ASP.NET Core 2.1及后续版本可用。其可用应用到控制器或者一个PageModel 类来告诉模型绑定,其目标为这个类的所有的public 属性。 [BindProperties(SupportsGet = true)] public class CreateModel : InstructorsPageModel { public Instructor Instructor { get; set; } } HTTP GET 请求的模型绑定 默认情况下,对于HTTP GET 请求,属性不会被绑定。经典的,对于一个HTTP GET 请求,所有你需要的只是一个记录 ID 参数。这个记录ID 会被使用来在数据库中查询这个条目。因此,没有必要将其绑定到持有模型实例的一个属性上。在你确实想要将属性绑定到来自于HTTP GET 请求的数据上时,可以将 SupportsGet 属性设置为 true。 [BindProperty(Name = "ai_user", SupportsGet = true)] public string ApplicationInsightsCookie { get; set; } 数据源 默认情况下,模型绑定以键值对的形式从一个HTTP 请求的如下数据源中获取数据:
对于每一个目标参数及属性,数据源都会以如上的顺序被扫描。但是还有一些例外:
如果默认源不正确,请使用如下属性之一来指定数据源:
这些属性:
public class Instructor { public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }
public void OnGet([FromHeader(Name = "Accept-Language")] string language) [FromBody] 属性 将[FromBody] 属性应用到一个属性上以从一个HTTP 请求体中填充这个属性值。ASP.NET Core 运行时代理了将读取请求体的数据到一个输入格式化器的职责。输入格式化器将在本章的后续进行解释。 当[FromBody]属性被应用到一个复杂类型参数上的时候,任何应用到这个属性的绑定源属性都会被忽略。如下的Create方法指定了它的pet 会被从请求体中进行填充。 public ActionResult<Pet> Create([FromBody] Pet pet)
Pet 类指定了其 Breed 属性将会从查询字符串中的值进行填充: public class Pet { public string Name { get; set; } [FromQuery] // Attribute is ignored. public string Breed { get; set; } } 在上述示例中:
输入格式化器仅仅读取请求体,其并不理解绑定源属性。如果一个合适的值在请求体中被发现,这个值将会被用来填充Breed 属性。 对于一个Action 方法,请不要应用[FromBody]属性给多于一个的属性。一旦请求流被一个输入格式化器读取,它便不再可用被再次读取以绑定到其他 [FromBody]属性。 额外的源 源数据通过值提供器被提供给模型绑定系统。你可以编写并注册自定义的值提供器,其为模型绑定从其他源中获取数据。举个例子,你或许想从Cookies 或者会话状态中获取数据。为了从一个新数据源获取数据,你可以:
示例app包括了一个 value provider 和 factory 示例,其从cookies 中获取值。如下是 services.AddRazorPages() .AddMvcOptions(options => { options.ValueProviderFactories.Add(new CookieValueProviderFactory()); options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(System.Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(System.Guid))); }) .AddXmlSerializerFormatters(); 演示的代码将自定义的值提供器放置在内置的值提供器之后。为了使其成为列表中的第一个,调用 没有数据源的模型属性 默认情况下,如果一个模型属性没有找到对应的值,那么不会创建模型状态错误的。这个时候,属性会被设置为 null 或者默认值:
当在一个表单字段中为某个属性没有找到对应的绑定值,而需要模型状态为 非验证通过时,可以使用 请注意[BindRequired] 行为应用到模型绑定是从 posted 表单数据,而不是在请求体中的 JSON 或者 XML 数据,请求体数据通过 input formatters 进行处理。 类型转换错误 如果一个数据源被找到但是没有转换为目标类型,模型状态便会被标记为 invalid。目标参数或者属性会被设置为 null 或者默认值,就如同以上章节所提到的。 在一个具有 [ApiController] 属性的API 控制器中,invalid 模型状态导致了一个自动的 HTTP 400 响应。 在一个Razor 页面,会重新显示这个页面,并显示了一个错误信息。 public IActionResult OnPost() { if (!ModelState.IsValid) { return Page(); } _instructorsInMemoryStore.Add(Instructor); return RedirectToPage("./Index"); } 客户端验证会捕获大部分的异常数据,否则它们便会被提交到Razor 页面表单中。这些验证使得我们很难触发如上高亮显示的代码。示例app 包含了一个 Submit with Invalid Date 按钮,其将异常数据放在 Hire Date 字段中并提交数据。这个按钮演示了当转换错误发生时, 用来重新显示页面的代码是如何工作的。 当页面被上述的代码重新显示时,无效的输入不会显示在表单字段中,这是因为模型属性被设置为 null 或者默认值。无效的输入会出现在一个错误信息中,如果你想将异常数据显示在表单字段中,考虑将模型字段设置为字符串并手动进行数据转换。 如果你不想类型转换错误导致模型状态错误,我们推荐同样的策略。在那种情形下,将模型属性设置为一个字符串。 To be continued... |
请发表评论