ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)

  前面文章介绍了ASP.NET MVC中的模型绑定和验证功能,本着ASP.NET MVC没有魔法的精神,本章内容将从代码的角度对ASP.NET MVC如何完成模型的绑定和验证进行分析,已了解其原理。

  本文的主要内容有:
  ● ModelBinder
  ● ValuePrivoder
  ● ModelMetadata
  ● 简单模型与复杂模型
  ● 小结

ModelBinder

  ModelBinder是ASP. NET MVC用于模型绑定的核心组件,所有的ModelBinder都实现了IModelBinder接口,如下图:

  

  该接口只有一个方法,那就是根据控制器以及绑定上下文完成模型绑定。
  在ASP.NET MVC中有不同的ModelBinder,它们分别用于绑定不同类型的数据,如普通的.Net对象、HTTP上传的文件等。
  默认有以下5种ModelBinder:
  ● DefaultModelBinder:默认的模型绑定器,一般情况下从浏览器提交的请求都将使用默认处理器来绑定模型。
  ● HttpPostedFileBaseModelBinder:HTTP文件模型绑定
  ● ByteArrayModelBinder:绑定二进制数据。
  ● LinqBinaryModelBinder:将请求绑定到System.Data.Linq.Binary对象。参考: http://stephenwalther.com/archive/2009/02/25/asp-net-mvc-tip-49-use-the-linqbinarymodelbinder-in-your
  ● CancellationTokenModelBinder:提供了一个用于传播模型绑定操作取消的机制。

  所有的ModelBinder都被一个名为ModelBinderDictionary的字典进行管理,而这个字典就存在于Controller类型的定义中,如下图所示,它是一个被保护的内部属性,用于Controller执行时完成模型绑定:

  

  ModelBinderDictionary的定义:

  

  从ModelBinderDictionary的定义中可以看到它实现了以Type为Key、IModelBinder类型为值的字典接口以及集合接口,可以动态的根据ModelBinder的类型增减ModelBinder,除此之外还有一个DefaultBinder,在一般情况下其运行的实例如下:

  

  Controller中的ModelBinder字典包含了上面介绍的5个ModelBinder。更多关于自定义ModelBinder的内容可参考:https://www.cnblogs.com/Cwj-XFH/p/5977508.html

ValuePrivoder

  在前面的文章中介绍了ASP.NET MVC的模型绑定可以从Form Data、Query String以及Route Data等数据源中获取数据,其原因是针对每一个数据源都有一个专门的数据提供器来获取数据源的数据,在ASP.NET MVC中存在一个定义值提供器的接口IValueProvider:

  

  核心方法GetValue通过一个Key来获取值,ContainsPrefix则判断提供器所指的数据源中是否包含以指定字符串为前缀的Key。
  直接实现该接口的类型有3个:
  ● NameValueCollectionValueProvider:通过名称和值共同存储数据的集合,可以通过名称查找到一个或多个值。
  ● DictionaryValueProvider<TValue>:通过键值对存储数据的泛型字典集合,字典的Key是唯一的,换句话说通过Key最多只能找到一个值。
  ● ValueProviderCollection:一个特殊的值提供器,它包含了所有相关的值提供器,在模型绑定中就是这个列表通过遍历的方式,调用相关值提供器的获取值方法来完成数据获取的。

  之前说过针对不同的数据源都有一个特定值提供器,那么这些提供器是如何实现的呢?它们是根据特性分别继承NameValueCollectionValueProvider及DictionaryValueProvider<TValue>类型来实现的,其具体分类如下,一共有7种不同数据源的值提供器:
  ● NameValueCollectionValueProvider:
    ○ JQueryFormValueProvider:用于获取被Jquery格式化的Form值。
    ○ FormValueProvider:用于获取Form表单的值。
    ○ QueryStringValueProvider:用于获取查询字符串的值。
  ● DictionaryValueProvider<TValue>
    ○ ChildActionValueProvider:用于获取子Action方法的值。
    ○ JsonValueProvider:用于获取请求中以Json格式传输的值(注:没有该类型的值提供器,Json的值提供器直接由JsonValueProviderFactory创建一个DictionaryValueProvider<object>类型的字典值提供器)。
    ○ HttpFileCollectionValueProvider:用于从Http请求中的文件集合中获取文件数据。
    ○ RouteDataValueProvider:用于从Route Data中获取值
  所有的值提供器都是由对应的工厂创建的,在默认情况下ASP.NET MVC中存在以下7种值提供器工厂,刚好对应上面的7种值提供器,其实现接口定义如下:

  

  每一个工厂的GetValueProvider方法获取对应的值提供器。
  所有的提供器工厂在MVC中被一个名为ValueProviderFactories的类型维护,该类型以硬编码的方式维护了一个静态、只读的提供器工厂列表:

  

   运行时结果,一共有7个工厂:

  

  下图是ASP.NET MVC在未特殊配置的情况下Controller的运行状态:

  

 下图是发送Json格式Post请求的状态,ValueProvider中多了一个用来获取Json数据的字典值提供器:

 发起请求的内容:

  

  请求中的值提供器与之前的相比多了一个用于提供Json数据的DictionaryValueProvider<object>类型:

  

ModelMetadata

  Metadata译为元数据,是一种描述数据的数据,而这里加上了Model,那么就是说描述Model数据的数据,下图为ASP.NET MVC中的一个Model定义:

  

  从图中代码用语言可以这样描述:
  ● 该对象有3个属性。
  ● 其中UserName是String类型的,必填并且格式为Email格式,展示名称为用户名。
  ● Password以及ConfirmPassword都是String类型,且类型都为密码,它们除了展示名称不同外,ConfirmPassword还需要和Password相比较,如果不同则给出相应的错误提示。

  而在MVC里面是通过ModelMatedata来对Model进行描述的,先看一下ASP.NET MVC中的ModelMetadata类型:

  

    

  从中可以看到一些是否只读、是否必填、模型类型、属性(同样是ModelMetadata类型)、展示名称、是否是复杂类型等描述信息。换句话就是ASP.NET MVC通过ModelMetadata可以对模型的属性是否只读、是否必填、类型等相应信息进行描述,甚至还包含了模型验证器来完成合法性验证。
  这里需要注意的是模型类型本身通过一个ModelMetadata来描述,而类型的属性同样被ModelMetadata描述,就是说ModelMetadata描述类型的结构是与对应类结构有相同的层次。

  注:ModelMetadata涉及到View的渲染,关于View的内容会在后续文章中介绍。

简单模型与复杂模型

  在ModelMetadata类型中有一个名为IsComplexType的属性,用于表示该类型是否为复杂类型,那么什么是复杂类型?相对应的什么是简单类型?

  

  上图是IsComplexType的实现代码,其核心有两个点:
  1. 通过当前模型类型来获取一个转换器(注:TypeDescriptor是一个用来获取类型相关信息的类型,如特性、属性、事件等,当然也包括类型转换器,下图是TypeDescriptor部分定义)。

  

  

  2.  获取到类型转换器后,通过转换器判断该类型是否能够从字符串转换,如果能那么它就是简单类型否则为复杂类型。(下图为TypeConverter的部分定义)

  

  知道了简单类型与复杂类型的区别,那么它们在MVC中有什么意义呢?
  首先对于ASP.NET MVC来说,它接收到的Http请求无论Header、Body等它实质上都是字符串,那么根据Http协议从中取出来的数据也是字符串,但是对于MVC的Action方法来说,它接受的参数可能是字符串的,也可能是数字、时间等其它类型,所以这里需要一个从字符串到其它类型的转换过程,而简单类型的目的就在于MVC进行模型绑定时可以直接根据名称从ValueProvider中获取到值(一个字符串),然后通过类型转换器将该字符串转换成所需类型。
  下面就介绍一些“理所当然”的类型转换器:
  ● 数字:下图是数字转换器的基类,它的CanConvertFrom方法的实现直接硬编码了能够从string类型转换数字类型(不同数字类型有具体的实现,这里不再介绍)。

  

    ● 时间:同样的时间类型也能从字符串转换。

  

    ● 布尔:布尔类型能够从字符串转换。

  

  为什么说“理所当然”?因为在实际的开发中某action需要一个时间参数,那么在表单中填写或者通过一些js组件选择一个日期,然后提交到服务器这个填写的时间“就是”一个时间类型,填写的数字也就是数字类型,一切都是理所当然的。但是打着没有“魔法”的目的,需要知道的是,哪怕最简单的布尔类型实际上也进行了转换工作,下图就是Boolean转换器的转换代码:

  

  在MVC中简单模型和复杂模型绑定的方式是不同的,也很容易理解简单模型仅需要一个字符串就可以完成转换,而复杂模型还需要更多的操作。
  在ASP.NET MVC中还有一种用法,就是自定义将特殊格式的字符串转换成特定对象,常用的例子就是坐标(经纬度)的转换,下面将使用逗号分隔字符串的格式定义用户注册的RegisterViewModel:
  1、创建一个RegisterViewModel的转换器,继承TypeConverter类型并重载CanConvertFrom及ConvertFrom方法即可:

  

  2、使用TypeConverter特性在RegisterViewModel类型上使用该转换器:

  

  3、通过Postman模拟请求:

   

  注:model为action参数名称。
  Action能够正确的获取到数据:

  

小结

  本篇内容主要是介绍了ASP.NET MVC中模型绑定的主要组件与概念,ValueProvider提供数据、ModelMetadata描述数据、ModelBinder绑定数据,绑定数据过程中不可或缺的数据验证、转换功能分别对应了ModelValidation以及TypeConverter类型。下一篇文章将介绍ASP.NET MVC在Controller执行时如何结合这些组件实现模型绑定的逻辑。

PS.由于篇幅较长所以将模型绑定解析的内容分为两篇,下篇将尽快整理并发出。祝各位元宵快乐(*^_^*)

参考:
  http://stephenwalther.com/archive/2009/02/25/asp-net-mvc-tip-49-use-the-linqbinarymodelbinder-in-your
  https://www.cnblogs.com/Cwj-XFH/p/5977508.html

本文链接:http://www.cnblogs.com/selimsong/p/8484482.html

ASP.NET没有魔法——目录

原文地址:https://www.cnblogs.com/selimsong/p/8484482.html

时间: 2024-10-17 12:51:44

ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)的相关文章

[转] ASP.NET MVC 模型绑定的功能和问题

摘要:本文将与你深入探究 ASP.NET MVC 模型绑定子系统的核心部分,展示模型绑定框架的每一层并提供扩展模型绑定逻辑以满足应用程序需求的各种方法. 同时,你还会看到一些经常被忽视的模型绑定技术,并了解如何避免一些最常见的模型绑定错误. ASP.NET MVC 模型绑定通过引入自动填充控制器操作参数的抽象层.处理通常与使用 ASP.NET 请求数据有关的普通属性映射和类型转换代码来简化控制器操作. 虽然模型绑定看起来很简单,但实际上是一个相对较复杂的框架,由许多共同创建和填充控制器操作所需对

ASP.NET没有魔法——ASP.NET MVC Razor与View渲染

对于Web应用来说,它的界面是由浏览器根据HTML代码及其引用的相关资源进行渲染后展示给用户的结果,换句话说Web应用的界面呈现工作是由浏览器完成的,Web应用的原理是通过Http协议从服务器上获取到对应的Html代码以及相关资源,使得浏览器能够完成正确的呈现工作. ASP.NET MVC作为一个Web应用构建框架View承担了UI显示的功能,在开发过程中View以Action的名称命名,当用户的请求被路由到某一Action方法时,ASP.NET MVC将会根据Action的名称来获取到对应的V

ASP.NET没有魔法——ASP.NET MVC使用Oauth2.0实现身份验证

原文:ASP.NET没有魔法--ASP.NET MVC使用Oauth2.0实现身份验证 随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构).服务器与多种客户端的(如PC.移动.Web等),甚至还有需要以服务的形式开放给第三方的,身份验证这一功能已经演化为一个服务,很多大型应用中都有自己的身份验证服务器甚至集群,所以普通的身份验证方式已经不能满足需求. 在.Net领域中也有一些开源的身份验证服务器组件,如Identit

MVC模型绑定

视图: 1 @model RegisterViewModel 2 @{ 3 ViewData["Title"] = "Register"; 4 } 5 6 <h2>@ViewData["Title"]</h2> 7 8 <div class="row"> 9 <div class="col-md-4"> 10 <form asp-route-return

ASP.NET没有魔法——ASP.NET OAuth、jwt、OpenID Connect

上一篇文章介绍了OAuth2.0以及如何使用.Net来实现基于OAuth的身份验证,本文是对上一篇文章的补充,主要是介绍OAuth与Jwt以及OpenID Connect之间的关系与区别. 本文主要内容有: ● Jwt简介 ● .Net的Jwt实现 ● OAuth与Jwt ● .Net中使用Jwt Bearer Token实现OAuth身份验证 ● OAuth与OpenID Connect 注:本章内容源码下载:https://files.cnblogs.com/files/selimsong/

ASP.NET MVC——模型绑定

这篇文章我们来讲讲模型绑定(Model Binding),其实在初步了解ASP.NET MVC之后,大家可能都会产生一个疑问,为什么URL片段最后会转换为例如int型或者其他类型的参数呢?这里就不得不说模型绑定了.模型绑定是指,用浏览器以HTTP请求方式发送的数据来创建.NET对象的过程.每当定义具有参数的动作方法时,一直是在依赖着这种模型绑定过程. 准备项目 我们先来创建一个MVC项目,名叫MVCModels,并在Models文件夹中创建一个新的类文件Person. 1 using Syste

asp.net mvc 模型绑定太糙淡了

之前就偶尔遇到模型绑定失效的问题,一直怪自己技术渣渣 不懂人家 而已.最近又遇到 自定义类 绑定失败,看了大牛scot文档 也不得姐..&……**……&*……*…………*%%(此处一堆脏话) 干脆自己直接丰庄个扩展方法,解析request 反序列.效果倍儿好!再也不担心模型绑定失效了,参数我想怎么传就怎么穿,顺带避免了微软时间/date*****/ 的大坑 哇哈哈哈.---------------------既然别人提供的封装方法那么难使 为嘛不自己造个? 大不了一样糙蛋呗 oh yeah

ASP.NET MVC5 学习系列之模型绑定

一.理解 Model Binding Model Binding(模型绑定) 是 HTTP 请求和 Action 方法之间的桥梁,它根据 Action 方法中的 Model 类型创建 .NET 对象,并将 HTTP 请求数据经过转换赋给该对象. 为了理解 Model Binding 如何工作,我们来做个简单的Demo,像往常一样创建一个 MVC 应用程序,添加一个 HomeController,修改其中的 Index 方法如下: public ActionResult Index(int id

MVC 模型绑定

在WebForm,获取提交表单的值一般都是Request.Form["Title"]这样的方式.在MVC中,提供了模型绑定机制.让后台获取表单或Url中的参数变得更加简单. 一.基本模型绑定 你可以直接在参数中用字符串,整型变量,实体或者是List<实体>的方式获取表单提交的参数. 参数中的这些东西都是与表单中的Html控件的name属性一一对应的. public ActionResult PersonAdd(int Id) { return View(); } 例如以上代