ASP.NET Core MVC 模型绑定用法及原理

前言

查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC 中模型绑定是如何实现的,以及它的一些其他用法。

模型绑定的用途

通常情况下,我们在使用 MVC 框架的时候不需要关注模型绑定的相关功能,因为它是集成到 MVC 框架内部的,当我们在浏览器访问一个地址的时候,无论是 GET 还是 POST 访问,在映射到 Action 的过程中 MVC 框架已经自动的进行了对象或者是路由参数的绑定,这其中就是使用的模型绑定。

在 ASP.NET Core MVC 中,模型绑定分为简单模型绑定和复杂模型绑定。简单的模型绑定比如直接从 Form 表单或者 URL 路由数据中获取信息,然后应用到Action方法的各个参数上,复杂模型绑定的话可能就不是简单的转换到参数的值上面了,可能中间还会涉及到一些数据类型转换,模型分解,参数校验等。

下面来看一下模型绑定的一个示例:

假设我们有一个ViewModel对象叫 Person,它的代码如下:

public class Person{    
   public string Name { get; set; }    public int Age { get; set; } }

在这个 ViewModel 对象中,都是使用的一些很简单的类型,那么我们的 Action 进行如下的定义:

public class PersonController{[HttpPost][Route("~api/person/add")]   
    public IActionResult CreatePerson(Person person)        {      
       return Ok(person);} }

在上面的代码中,我们可以向 http://localhost:5000/api/person/add 这个地址发送一个 POST 请求,Body 类型使用普通的 Form ,参数使用上面Person定义的 NameAge。 在 Action 上添加断点,我们就可以看到person变量中的值,然后此 Action 会返回一个被 json 序列化后的结果对象。

这样一个过程,就是模型绑定在实际开发中的一个用途和用法。

是不是看起来很简单呢?但是在内部模型绑定子系统是比较复杂的,由很多部分组成。

模型绑定的一些用法

在 ASP.NET Core MVC 中,支持以下表单类型的模型绑定。

[FromHeader][FromQuery][FromRoute][FromForm]

示例:

public IActionResult CreatePerson([FromForm]Person person){    
    return Ok(person); }

这些 [FromXXX] 是告诉模型绑定在解析的过程中从HttpContext中那一部分获取信息。

还有一部分模型绑定框架中定义的一些 Attribute 是用来在模型模型的过程中限制或者忽略一些参数。比如

[BindRequired][BindNever]

这两个是用来在模型绑定的过程中添加的一些制约,BindRequired可以应用在类或者属性上,用来限制在绑定的过程中必须需要的一些值。BindNever用来忽略当前参数的绑定。

还有两个 [FromServices][FromBody]

[FromServices] 是参数绑定的过程中,告诉模型绑定框架该参数从 DI 容器中获取。

[FromBody] 是参数绑定的过程中,告诉框架该参数是使用配置过的格式化程序从Http Body 中解析。

在[FromBody]中,默认情况下会使用MVC框架内部配置的 JsonInputFormatter 进行反序列化解析,如果你传递的 Body 中的类型是 application/xml,你可以在 ConfigureServices 方法中配置 services.AddMvc().AddXmlSerializerFormatters();进行XML的反序列化。

还有一个 [ModelBinder] 这个可能很多人没用过,这个是应用在 Controller 中的属性上的,用来绑定属性信息。比如

public class Controller{[ModelBinder]  
   public string Name { get; set;}    
   public IActionResult CreatePerson(Person person)        {
      return Ok(person);} }

在 Action 激活的时候,Controller 的 Name 属性也会具有Action 参数中 person 变量中名字和 Name 相同参数的值。

自定义模型绑定

自定义模型绑定属于 MVC 模型绑定的一些高级知识,在一些特殊情况中我们可能会使用到他们。 详情可以参考这篇 文章 https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding,本文不打算做过多介绍。

MVC 模型绑定(ModelBinding)实现原理

看过我 ASP.NET Core MVC Action 激活 这篇文章的同学应该知道,在 Action 激活的过程中会涉及到很多状态,那么模型绑定是在 ActionBegin 这个状态中进行的,同时在 ActionNext 过程中被使用,我就直接接着这篇文章的 Action 执行部分进行讲解了。

首先,模型绑定的入口位于 ControllerActionInvoker 这个类中的一个Next方法里

private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted){......    switch (next){        case State.ActionBegin:             BindArgumentsAsync();            
      goto case State.ActionNext;        case State.ActionNext:_actionExecutingContext = new ActionExecutingContext(_arguments);}...... }

可以看到在 State.ActionBegin 这个过程中使用了 BindArgumentsAsync() 进行参数绑定,那么整个绑定过程就由此开始。

在 Core MVC 框架模型绑定部分源码中,模型绑定源码分为这几部分:元数据(Metadata),值提供器(ValueProvider),验证者(Validator),绑定器(Binder)

元数据:元数据相当于模型绑定中的实体对象,用来储存在整个模型绑定子系统中需要的各种必要信息和元数据信息,包括模型参数的元数据和以及绑定信息的元数据等等。

值提供器:值提供器用来提供在运行时模型绑定器可以从中提取值的一个Provider, 默认情况下,值提供器会从以下地方提取各种资源的值:

1、以前绑定的操作参数(当该操作为子操作时)

2、表单字段 (FormValueProvider)

3、路由数据 (RouteValueProvider)

4、查询字符串参数 (QueryStringValueProvider)

5、JQuery 表单数据(JQueryFormValueProvider)

这些 ValueProvider 对象由 CompositeValueProvider 进行统一管理,它是一个集合用来创建或者获取这些 ValueProvider 对象。

验证者:验证者是用来验证Action参数中模型字段的合法性。

它的默认实现是 DefaultObjectValidator,它会根据元数据信息来确定绑定过程中使用的具体 ValudatorProvider 对象,ValudatorProvider 是用来提供IModelValidator接口实例对象的。

在 ASP.NET Core MVC 中有两个类实现了 IModelValidator接口,他们分别是 DataAnnotationsModelValidator 和 ValidatableObjectAdapter。 其中 ValidatableObjectAdapter 是一个适配器,用来转换和封装验证结果,所以我们主要看 DataAnnotationsModelValidator

DataAnnotationsModelValidator 就是用来验证模型类中的各种 Attribute 的,也就是DataAnnotations相关的那些类,比如 [MaxLength][Required][RegularExpression] 等等。

绑定器: 绑定器用来绑定Action参数中的大多数简单和复杂的数据模型,它通过对模型各个属性使用递归逻辑来实现该目标。

针对不同类型的绑定具有不同的绑定器,如:

ArrayModelBinderSimpleTypeModelBinderFormFileModelBinder
FormCollectionModelBinderComplexTypeModelBinder
BodyModelBinderHeaderModelBinderDictionaryModelBinder
ServicesModelBinderKeyValuePairModelBinderByteArrayModelBinder
CancellationTokenModelBinderCollectionModelBinderBinderTypeModelBinder

这些 ModelBinder 对象都具有各自的 Provider 对象,对来返回当前Binder对象的实例对象。

这些 ModelBinder 对象由 ParameterBinder 进行统一管理, ParameterBinder 对象会接收模型的元数据信息(Metadata),绑定器工厂(BinderFactory),验证者(Validator),然后进行最终的模型绑定流程,如下图:

回到总流程

我们回到 ControllerActionInvoker 这个中,BindArgumentsCoreAsync 这个函数中会进行上层的模型绑定,流程如下:

以上总流程的前两步就是 MVC Core 模型绑定系统针对于 Controller 中 Action 参数的绑定,实际上除了针对 Action 参数绑定外,还会对 Controller 中的使用了 [IModelBinder] 特性的进行绑定,那么第三步就是在做这个事情。

至此,模型绑定流程结束。

总结

在本篇中,我们学习了在 ASP.NET Core MVC 中模型绑定的一些用途和一些基本的用法,然后学习了 MVC框架中模型绑定的一些内部实现原理,通过这个学习我们可以对整个模型绑定系统更加的系统的一个了解,以便于我们有在工作系统的时候可以针对于模型绑定系统进行扩展。

原文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-modelbinding.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA无法加载log文件

如图所示,无论怎么生成log文件,idea文件列表始终不显式 解决方法 打开setting 打开File Types 选择文本文档Text,添加后缀*.log

使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】

一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于 sqlserver,mysql的集群方案,当然还可以在第三方插件的基础上实现q…

一个正则表达式酿成的惨案

转载自 一个正则表达式酿成的惨案 导读:正则表达式是程序员经常使用的工具之一。本文作者通过一个正则表达式的陷阱,先深入剖析了出现问题的原因,后给出怎么处理这类问题的方法。最后还给出了一些检测常见正则表达式问题的工具&#xff0c…

详解C# Tuple VS ValueTuple(元组类 VS 值元组)

C# 7.0已经出来一段时间了,大家都知道新特性里面有个对元组的优化:ValueTuple。这里利用详尽的例子详解Tuple VS ValueTuple(元组类VS值元组),10分钟让你更了解ValueTuple的好处和用法。 如果您对Tuple足够了解&#…

Eclipse把默认为Gbk的编码变为UTF-8

菜单栏Windows–>Preferences,左侧导航栏展开General–>Workspace,修改左下角的Text file encoding,选中Other改为UTF-8即可

从LINQ开始之LINQ to Objects(上)

LINQ概述 LINQ,语言集成查询(Language Integrated Query),它允许使用C#或VB代码以查询数据库相同的方式来操作不同的数据源。 1.LINQ体系结构 从上图可以看出,LINQ总共包括五个部分:LINQ to Objects、LINQ to DataSets、LINQ to …

单点登录终极方案之 CAS 应用及原理

转载自 单点登录终极方案之 CAS 应用及原理 Cookie的单点登录的实现方式很简单,但是也问题颇多。例如:用户名密码不停传送,增加了被盗号的可能。另外,不能跨域! 1、基于Cookie的单点登录的回顾 基于Cookie的单点登录…

微软亚太区资料科学总监:R 语言是 VS 生态第一顺位

微软亚太区资料科学总监Graham Williams 微软在2015年并购R语言工具商Revolution Analytics之后,随即在2016年,也开始在自家主力开发工具Visual Studio上,支持R语言。微软将如何定位R语言在微软开发工具链的位置?微软亚太区资料科…

java中如何数组是如何赋值的?

由于数组是引用类型,故无法与变量赋值的方式一样,int a 10;int b a; 那么数组是如何赋值的呢? 是这样赋值的: public static void arrayFuZhi(){//八斤的身高和体重int [] ba {170,80};//九斤的身高和体重与八斤的一样int [] …

从LINQ开始之LINQ to Objects(下)

前言 上一篇《从LINQ开始之LINQ to Objects(上)》主要介绍了LINQ的体系结构、基本语法以及LINQ to Objects中标准查询操作符的使用方法。 本篇则主要讨论LINQ to Objects中的扩展方法以及延迟加载等方面的内容。 扩展方法 1.扩展方法简介 扩展方法能够向…

Localdatetime

根据指定日期/时间创建对象 LocalDate localDate LocalDate.of(2018, 1, 13); LocalTime localTime LocalTime.of(9, 43, 20); LocalDateTime localDateTime LocalDateTime.of(2018, 1, 13, 9, 43, 20); System.out.println(localDate); System.out.println(localTime); Sy…

基于OAuth2的认证(译)

OAuth 2.0 规范定义了一个授权(delegation)协议,对于使用Web的应用程序和API在网络上传递授权决策非常有用。OAuth被用在各钟各样的应用程序中,包括提供用户认证的机制。这导致许多的开发者和API提供者得出一个OAuth本身是一个认证…

Redis非阻塞I/O多路复用机制

小曲在S城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。 经营方式一 客户每送来一份快递,小曲就让一个快递员盯着,然后快递员开车去…

React前端格式化时间

import moment from "moment";const dateFormat YYYY-MM-DD HH:mm:ss;<DatePicker label"时间" name"insertTime" showTime onChange{onChange} onOk{onOk}defaultValue{moment(location?.defaultValues?.record?.insertTime, dateFormat…

[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)

1 什么是OIDC&#xff1f; 看一下官方的介绍&#xff08;http://openid.net/connect/&#xff09;&#xff1a; OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the a…

EasyExcel中输出为时间格式

前端传值 后端Excel配置为String类型 配置为其他类型显示格式转化异常

Identity Service - 解析微软微服务架构eShopOnContainers(二)

接上一篇&#xff0c;众所周知一个网站的用户登录是非常重要&#xff0c;一站式的登录&#xff08;SSO&#xff09;也成了大家讨论的热点。微软在这个Demo中&#xff0c;把登录单独拉了出来&#xff0c;形成了一个Service&#xff0c;用户的注册、登录、找回密码等都在其中进行…

TCP/IP协议——ARP详解

转载自 TCP/IP协议——ARP详解 本文主要讲述了ARP的作用、ARP分组格式、ARP高速缓存、免费ARP和代理ARP。 1.学习ARP前要了解的内容 建立TCP连接与ARP的关系 应用接受用户提交的数据&#xff0c;触发TCP建立连接&#xff0c;TCP的第一个SYN报文通过connect函数到达IP层&a…