目錄 一、ValidationAttribute本身是可以應用到參數上的 二、為什麼需要基于參數的Model驗證? 三、如何得到應用在參數上的ValidationAttribute? 四、自定義ModelValidatorProvider 五、自定義ModelBinder 六、執行個體示範
如果你夠細心應該會發現我們常用的驗證特性都可以直接應用到方法的參數上。以如下所示的RangeAttribute的定義為例,應用在該類型上的AttributeUsageAttribute的定義表明可以标注該特性的目标元素包括參數、字段和屬性。
但是對于ASP.NET MVC的Model驗證來說,應用在Action方法參數上的驗證特性起不到任何作用,原因很簡單:用于進行Model驗證的ModelValidator對象是通過基于參數類型的Model中繼資料來建立的,根本不會去解析應用在參數本身上的驗證特性。
但是在我看到,直接針對Action參數的Model驗證具有很高的實用意義:
有些情況下我們不能對作為Model的資料類型進行修改(比如像int、double和字元串這樣的原生類型);
相同的Model類型在不同的Action方法調用中需要采用不同的驗證規則。
如果我們可以直接将驗證特性應用到參數上面,這兩個問題在一定程度上都可以得到解決。
到目前為止,我們對ASP.NET MVC的可擴充的Model驗證系統已經有了一個全面的了解,現在我們通過對它進行相應的擴充使直接應用到參數上的驗證特性能夠生效。我們需要自定義一個ModelValidatorProvider将提供基于應用到參數上的驗證特性的ModelValidator,但在這之前需要解決的另一個問題是如何将應用于參數的特性提供給我們自定義的ModelValidatorProvider。在這裡我們将目前ControllerContext作為這些特性的載體。
Action方法的執行通過ActionInvoker來實作,預設的ControllerActionInvoker和AsyncControllerActionInvoker都定義了一個受保護的虛方法GetParameterValue根據用于描述參數的ParameterDescriptor對象和目前的Controller上下文來綁定對應的參數值。那麼我們就可以通過繼承ControllerActionInvoker/AsyncControllerActionInvoker以重寫該方法的方式将ParameterDescriptor儲存目前的Controller上下文中。
為此我們定義了一個具有如下定義的兩個自定義的ActionInvoker。ParameterValidationActionInvoker和ParameterValidationAsyncActionInvoker分别繼承自ControllerActionInvoker和AsyncControllerActionInvoker。在重寫的GetParameterValue方法中,我們在調用基類的同名方法之前将作為參數的ParameterDescriptor對象儲存到目前Controller上下文中,具體來說是放到了表示目前路由資料的RouteDataDictionary對象的DataTokens集合中。在方法調用之後我們将它從Controller上下文中移除。
ParameterValidationActionInvoker/ParameterValidationAsyncActionInvoker存放到目前Controller上下文中的ParameterDescriptor被我們自定義的ModelValidatorProvider提取出來用于建立相應的ModelValidator。如下面的代碼片斷所示,我們自定義的ParameterValidationModelValidatorProvider直接繼承自DataAnnotationsModelValidatorProvider,在重寫的GetValidators方法中我們将ParameterDescriptor從Controller上下文中提取出來,然後得到應用在參數上的所有的特性并與目前的特性清單進行合并,最後将合并的特性清單作為參數調用積累的GetValidators方法。
值得一提的是,應用在參數上的特性是針對最外層的容器類型,而不是針對容器類型的屬性的。比如是以我們在類型為Contact的參數上應用一個驗證特性,該特性應該與應用在Contact類型上的特性具有相同的效果,但是與Address屬性無關。是以ParameterDescriptor的提取以及特性的合并僅僅在目前Model中繼資料的ContainerType為Null的情況下才會進行。除此之外,我們還利用應用到參數的DisplayAttribute特性對Model中繼資料的DisplayName屬性進行了相應的設定。
在預設的情況下,隻有在針對複雜類型的Model綁定過程中才會進行Model驗證。雖然我們通過ParameterValidationModelValidatorProvider能夠根據應用在Action方法參數上的驗證特性生成相應的ModelValidator,但是如果驗證特性是應用在一個簡單類型的參數上,生成出來的ModelValidator也是不會被使用的。為了使Model驗證發生在針對簡單類型的Model綁定過程中,我們不得不建立一個自定義的ModelBinder。為此我們定義了一個具有如下定義的ParameterValidationModelBinder,它直接繼承自DefaultModelBinder,而針對簡單類型的Model驗證定義在重寫的BindModel方法中。
到此為止,為了能夠将驗證特性應用于Action方法的參數,我們建立了自定義的ActionInvoker、ModelValidatorProvider和ModelBinder。為了驗證它們是否能夠最終實作我們期望的驗證效果,我們将它們應用到一個簡單的ASP.NET MVC應用中。
在通過Visual Studio的ASP.NET MVC項目模闆建立的空的Web應用中,我們建立了一個具有如下定義的HomeController。我們重寫了CreateActionInvoker方法,如果調用基類同名方法傳回一個ControllerActionInvoker對象,那麼我們傳回一個ParameterValidationActionInvoker對象,否則傳回一個ParameterValidationAsyncActionInvoker對象,這是與預設的同步/異步Action執行方式保持一緻。
Action方法Add表示一個用于進行加法運算的操作,表示操作數的兩個參數x和y分别應用了一個RangeAttribute特性将允許值得範圍設定為10到20和20到30,并設定了相應的錯誤消息。此外,兩個參數還通過應用ModelBinderAttribute特性使我們自定義的ParameterValidationModelBinder參與到這兩個參數Model綁定中。DisplayAttribute特性也應用到這兩個參數上對顯示名稱進行了相應的設定。作于執行加法運算後的結果通過預設的View呈現出來。下面的代碼片斷表示Action方法Add對應的View的定義,這是一個Model類型為double的強類型View。我們通過一個ValidationSummary來呈現驗證的錯誤消息,隻有在驗證成功的情況下我們才真正顯示運算的結果。
然後我們在Global.asax中對自定義的ParameterValidationModelValidatorProvider進行注冊。如下面的代碼片斷所示,在注冊ParameterValidationModelValidatorProvider之前需要将現有的DataAnnotationsModelValidatorProvider移除。
我們運作該程式通過在浏覽器中輸入相應的位址來通路定義在HomeController中的Add操作,并以查詢字元串的形式指定該Action方法的兩個操作數(x=9,y=31)。由于提供的參數不服務應用在參數上的 RangeAttribute所定義的驗證規則,如下圖所示的錯誤消息會自動呈現出來。

<a href="http://www.cnblogs.com/artech/archive/2012/06/06/data-annotations-model-validation-01.html">ASP.NET MVC基于标注特性的Model驗證:ValidationAttribute</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/07/data-annotations-model-validation-02.html">ASP.NET MVC基于标注特性的Model驗證:DataAnnotationsModelValidator</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/08/data-annotations-model-validation-03.html">ASP.NET MVC基于标注特性的Model驗證:DataAnnotationsModelValidatorProvider</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/11/data-annotations-model-validation-04.html">ASP.NET MVC基于标注特性的Model驗證:将ValidationAttribute應用到參數上</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/12/data-annotations-model-validation-05.html">ASP.NET MVC基于标注特性的Model驗證:一個Model,多種驗證規則</a>