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

来源:https://www.cnblogs.com/selimsong/archive/2018/03/02/8484482.html
-Advertisement-
Play Games

前面文章介紹了ASP.NET MVC中的模型綁定和驗證功能,本著ASP.NET MVC沒有魔法的精神,本章內容將從代碼的角度對ASP.NET MVC如何完成模型的綁定和驗證進行分析,已瞭解其原理。 本文的主要內容有: ● ModelBinder ● ValuePrivoder ● ModelMeta ...


  前面文章介紹了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沒有魔法——目錄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在eclipse中,使用svn導入svn上的一個maven項目,但是導入後類的包並沒有以源碼包的方式顯示,而是以普通文件包的方式顯示出來,在對類進行F3等操作時就報錯:“the resource is not on the build path of a java project”。 這種情況的 ...
  • Maven是優秀的Java項目對象模型解決方案,意為知識的積累(意地緒文),Maven可以方便的解決Java項目包依賴問題,通過配置pom.xml引入依賴,並自動引入其他依賴。 操作系統版本:windows 7 Eclipse版本:Oxygen.1a(4.7.1a) Maven版本:3.5.2 JD ...
  • if 語句結構 if 判斷條件: 要執行的代碼 判斷條件:一般為關係表達式或者bool類型的值 執行過程:程式運行到if處,首先判斷if所帶的條件,如果條件成立,就返回True,則執行if所帶的代碼;如果條件不成立,就返回值是False, 跳過if語句繼續向下執行。 示例1: 在控制台應用程式中輸入 ...
  • 簡介 IMapControl介面最重要的是包含IMap屬性,還提供另外的屬性用於:管理一般的外觀顯示,管理圖層,載入地圖文檔以及跟蹤在界面上顯示的圖形。 小知識點:一般一個介面的最新實現是 Default ,如果又多個實現,是通過數字區分,數字越大,其版本也越新。 屬性介紹 1. IActiveVi ...
  • 目錄 xBIM WeXplorer 簡要介紹 xBIM WeXplorer xViewer 基本應用 xBIM WeXplorer xViewer 瀏覽器檢查 xBIM WeXplorer xViewer的導航,相機、剖切、隱藏 等操作 xBIM WeXplorer 設置模型顏色 xBIM 綜合使用 ...
  • https://www.cnblogs.com/xishuai/p/asp-net-5-owin-katana.html http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/chapter-eight.html http://wiki.j ...
  • 目的 因為某些原因需要將存放在 Google Chrome 內的書簽導出到本地,所幸 Google Chrome 提供了導出書簽的功能。 分析 首先在 Google Chrome 瀏覽器當中輸入 來到書簽管理頁面,找到最右側的三個點,選擇導出書簽,導出的文件是一個 HTML 文件,裡面包含了所有書簽 ...
  • Power Shell管理Office參考http://www.mamicode.com/info-detail-494553.html C#調用Power Shell 參考 https://www.cnblogs.com/chenkai/archive/2010/11/09/1872471.htm ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...