天天看點

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

  前面文章介紹了ASP.NET MVC中的模型綁定和驗證功能,本着ASP.NET MVC沒有魔法的精神,本章内容将從代碼的角度對ASP.NET MVC如何完成模型的綁定和驗證進行分析,已了解其原理。

  本文的主要内容有:

  ● ModelBinder

  ● ValuePrivoder

  ● ModelMetadata

  ● 簡單模型與複雜模型

  ● 小結

  ModelBinder是ASP. NET MVC用于模型綁定的核心元件,所有的ModelBinder都實作了IModelBinder接口,如下圖:

  

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

  該接口隻有一個方法,那就是根據控制器以及綁定上下文完成模型綁定。

  在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執行時完成模型綁定:

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

  ModelBinderDictionary的定義:

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

  從ModelBinderDictionary的定義中可以看到它實作了以Type為Key、IModelBinder類型為值的字典接口以及集合接口,可以動态的根據ModelBinder的類型增減ModelBinder,除此之外還有一個DefaultBinder,在一般情況下其運作的執行個體如下:

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

  Controller中的ModelBinder字典包含了上面介紹的5個ModelBinder。更多關于自定義ModelBinder的内容可參考:https://www.cnblogs.com/Cwj-XFH/p/5977508.html

  在前面的文章中介紹了ASP.NET MVC的模型綁定可以從Form Data、Query String以及Route Data等資料源中擷取資料,其原因是針對每一個資料源都有一個專門的資料提供器來擷取資料源的資料,在ASP.NET MVC中存在一個定義值提供器的接口IValueProvider:

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

  核心方法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種值提供器,其實作接口定義如下:

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

  每一個工廠的GetValueProvider方法擷取對應的值提供器。

  所有的提供器工廠在MVC中被一個名為ValueProviderFactories的類型維護,該類型以寫死的方式維護了一個靜态、隻讀的提供器工廠清單:

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

   運作時結果,一共有7個工廠:

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

  下圖是ASP.NET MVC在未特殊配置的情況下Controller的運作狀态:

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

 下圖是發送Json格式Post請求的狀态,ValueProvider中多了一個用來擷取Json資料的字典值提供器:

 發起請求的内容:

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

  請求中的值提供器與之前的相比多了一個用于提供Json資料的DictionaryValueProvider<object>類型:

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

  Metadata譯為中繼資料,是一種描述資料的資料,而這裡加上了Model,那麼就是說描述Model資料的資料,下圖為ASP.NET MVC中的一個Model定義:

   

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

  從圖中代碼用語言可以這樣描述:

  ● 該對象有3個屬性。

  ● 其中UserName是String類型的,必填并且格式為Email格式,展示名稱為使用者名。

  ● Password以及ConfirmPassword都是String類型,且類型都為密碼,它們除了展示名稱不同外,ConfirmPassword還需要和Password相比較,如果不同則給出相應的錯誤提示。

  而在MVC裡面是通過ModelMatedata來對Model進行描述的,先看一下ASP.NET MVC中的ModelMetadata類型:

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

    

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

  從中可以看到一些是否隻讀、是否必填、模型類型、屬性(同樣是ModelMetadata類型)、展示名稱、是否是複雜類型等描述資訊。換句話就是ASP.NET MVC通過ModelMetadata可以對模型的屬性是否隻讀、是否必填、類型等相應資訊進行描述,甚至還包含了模型驗證器來完成合法性驗證。

  這裡需要注意的是模型類型本身通過一個ModelMetadata來描述,而類型的屬性同樣被ModelMetadata描述,就是說ModelMetadata描述類型的結構是與對應類結構有相同的層次。

  注:ModelMetadata涉及到View的渲染,關于View的内容會在後續文章中介紹。

  在ModelMetadata類型中有一個名為IsComplexType的屬性,用于表示該類型是否為複雜類型,那麼什麼是複雜類型?相對應的什麼是簡單類型?

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

  上圖是IsComplexType的實作代碼,其核心有兩個點:

  1. 通過目前模型類型來擷取一個轉換器(注:TypeDescriptor是一個用來擷取類型相關資訊的類型,如特性、屬性、事件等,當然也包括類型轉換器,下圖是TypeDescriptor部分定義)。

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

  2.  擷取到類型轉換器後,通過轉換器判斷該類型是否能夠從字元串轉換,如果能那麼它就是簡單類型否則為複雜類型。(下圖為TypeConverter的部分定義)

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

  知道了簡單類型與複雜類型的差別,那麼它們在MVC中有什麼意義呢?

  首先對于ASP.NET MVC來說,它接收到的Http請求無論Header、Body等它實質上都是字元串,那麼根據Http協定從中取出來的資料也是字元串,但是對于MVC的Action方法來說,它接受的參數可能是字元串的,也可能是數字、時間等其它類型,是以這裡需要一個從字元串到其它類型的轉換過程,而簡單類型的目的就在于MVC進行模型綁定時可以直接根據名稱從ValueProvider中擷取到值(一個字元串),然後通過類型轉換器将該字元串轉換成所需類型。

  下面就介紹一些“理所當然”的類型轉換器:

  ● 數字:下圖是數字轉換器的基類,它的CanConvertFrom方法的實作直接寫死了能夠從string類型轉換數字類型(不同數字類型有具體的實作,這裡不再介紹)。

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

    ● 時間:同樣的時間類型也能從字元串轉換。

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

    ● 布爾:布爾類型能夠從字元串轉換。

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

  為什麼說“理所當然”?因為在實際的開發中某action需要一個時間參數,那麼在表單中填寫或者通過一些js元件選擇一個日期,然後送出到伺服器這個填寫的時間“就是”一個時間類型,填寫的數字也就是數字類型,一切都是理所當然的。但是打着沒有“魔法”的目的,需要知道的是,哪怕最簡單的布爾類型實際上也進行了轉換工作,下圖就是Boolean轉換器的轉換代碼:

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

  在MVC中簡單模型和複雜模型綁定的方式是不同的,也很容易了解簡單模型僅需要一個字元串就可以完成轉換,而複雜模型還需要更多的操作。

  在ASP.NET MVC中還有一種用法,就是自定義将特殊格式的字元串轉換成特定對象,常用的例子就是坐标(經緯度)的轉換,下面将使用逗号分隔字元串的格式定義使用者注冊的RegisterViewModel:

  1、建立一個RegisterViewModel的轉換器,繼承TypeConverter類型并重載CanConvertFrom及ConvertFrom方法即可:

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

  2、使用TypeConverter特性在RegisterViewModel類型上使用該轉換器:

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

  3、通過Postman模拟請求:

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

 

  注:model為action參數名稱。

  Action能夠正确的擷取到資料:

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

  本篇内容主要是介紹了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沒有魔法——目錄

作者:7m魚

出處:http://www.cnblogs.com/selimsong/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

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

繼續閱讀