ASP.NETMVC Model驗證(四)
前言
本篇主要講解ModelValidatorProvider和ModelValidator兩種類型的自定義實作,前者是Model驗證提供程式,而ModelValidator類型則是Model驗證執行類型,在下面的示例中會使用Model驗證提供程式結合Model驗證執行類型來執行Model驗證,就是使用上個篇幅中所講的實作個Model綁定器繼承自DefaultModelBinder類型,在自定義Model綁定器中使用ModelValidator類型。
Model驗證
Model驗證簡單運用示例
ModelValidator使用生成過程
自定義實作DefaultModelBinder進行驗證
自定義ModelValidatorProvider和ModelValidator
ValidationAttribute特性類使用
自定義ValidationAttribute特性類的示例實作
自定義ModelValidatorProvider和ModelValidator
ModelValidator
深呼吸一下,直接進入主題。
在我們定義自定義的ModelValidator類型之前,先看一下ModelValidator類型的定義,代碼1-1.
代碼1-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<code> </code><code>public</code> <code>abstract</code> <code>class</code> <code>ModelValidator</code>
<code> </code><code>{</code>
<code> </code><code>protectedModelValidator(ModelMetadata metadata, ControllerContext controllerContext);</code>
<code> </code><code>protectedinternal ControllerContextControllerContext { </code><code>get</code><code>; }</code>
<code> </code><code>public</code> <code>virtual</code> <code>boolIsRequired { </code><code>get</code><code>; }</code>
<code> </code><code>//</code>
<code> </code><code>// 摘要:</code>
<code> </code><code>// 擷取模型驗證程式的中繼資料。</code>
<code> </code><code>// 傳回結果:</code>
<code> </code><code>// 模型驗證程式的中繼資料。</code>
<code> </code><code>protected</code> <code>internal</code> <code>ModelMetadata Metadata { </code><code>get</code><code>;}</code>
<code> </code><code>public</code> <code>virtual</code> <code>IEnumerable<ModelClientValidationRule>GetClientValidationRules();</code>
<code> </code><code>public</code> <code>static</code> <code>ModelValidatorGetModelValidator(ModelMetadata metadata, ControllerContext context);</code>
<code> </code><code>// 在派生類中實作時驗證對象。</code>
<code> </code><code>// 參數:</code>
<code> </code><code>// container:</code>
<code> </code><code>// 容器。</code>
<code> </code><code>// 驗證結果的清單。</code>
<code> </code><code>public</code> <code>abstract</code> <code>IEnumerable<ModelValidationResult> Validate(</code><code>object</code> <code>container);</code>
<code> </code><code>}</code>
代碼1-1中,我們可以看到ModelValidator類型的構造是可以重寫的,這樣便于我們自定義一些所需的資料,不過下面的示例中并沒有這麼做,目的是在于簡單的示範,然後我們再看一下Metadata屬性,這個屬性非常重要了,它表示封裝着所要進行驗證的Model或者是Model屬性的資訊在此提醒大家關注一下,最後要看的就是Validate()抽象方法了,我們自定義類型必須實作的這麼一個方法,在它的傳回類型中有個ModelValidationResult類型,我們就來看一下ModelValidationResult類型,代碼1-2.
代碼1-2
<code> </code><code>public</code> <code>class</code> <code>ModelValidationResult</code>
<code> </code><code>publicModelValidationResult();</code>
<code> </code>
<code> </code><code>// 擷取或設定成員名。</code>
<code> </code><code>// 成員名。</code>
<code> </code><code>public</code> <code>string</code> <code>MemberName { </code><code>get</code><code>;</code><code>set</code><code>; }</code>
<code> </code><code>// 擷取或設定驗證結果消息。</code>
<code> </code><code>// 驗證結果消息。</code>
<code> </code><code>public</code> <code>string</code> <code>Message { </code><code>get</code><code>;</code><code>set</code><code>; }</code>
正如大家所看到的,代碼1-2沒啥好說的,看完本篇自然明白。
ASP.NET MVC Model驗證(三)和ASP.NET MVCModel驗證(一)中的示例想必有的朋友是看過了,沒看過的朋友建議去快速的看一下篇幅一和篇幅三的示例部分(本篇示例的基礎依賴上篇的基礎部分,篇幅長度原因這裡就不再次說明了),在這裡我想說的是Model驗證也是分級别的,就是上篇中也有提到過的Model級驗證和Model屬性級驗證,在下面的示例中,我把上篇篇幅中的驗證過程拆成三個部分(可以寫在一起,便于了解)。
我把第一個部分定義為Model屬性級驗證,我們來看一下示例代碼1-3。
代碼1-3
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<code> </code><code>public</code> <code>class</code> <code>MyCustomModelPropertyValidator : ModelValidator</code>
<code> </code><code>publicMyCustomModelPropertyValidator(ModelMetadatamodelMetadata, ControllerContextcontrollContext)</code>
<code> </code><code>: </code><code>base</code><code>(modelMetadata,controllContext)</code>
<code> </code><code>{ }</code>
<code> </code><code>public</code> <code>override</code> <code>IEnumerable<ModelValidationResult> Validate(</code><code>object</code> <code>container)</code>
<code> </code><code>{</code>
<code> </code><code>switch</code><code>(Metadata.PropertyName)</code>
<code> </code><code>{</code>
<code> </code><code>case</code><code>"ID"</code><code>:</code>
<code> </code><code>if</code><code>(</code><code>string</code><code>.IsNullOrEmpty((</code><code>string</code><code>)container)|| (</code><code>string</code><code>)container == </code><code>""</code><code>)</code>
<code> </code><code>{</code>
<code> </code><code>return</code> <code>new</code> <code>ModelValidationResult[]</code>
<code> </code><code>{ </code>
<code> </code><code>new</code> <code>ModelValidationResult()</code>
<code> </code><code>{</code>
<code> </code><code>MemberName=</code><code>"ID"</code><code>,</code>
<code> </code><code>Message= </code><code>"請輸入ID,ID不能為空!"</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>}</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>case</code><code>"UserID"</code><code>:</code>
<code> </code><code>MemberName=</code><code>"UserID"</code><code>,</code>
<code> </code><code>Message= </code><code>"請輸入使用者賬戶,使用者賬戶不能為空!"</code>
<code> </code><code>case</code><code>"Password1"</code><code>:</code>
<code> </code><code>ControllerContext.Controller.TempData.Add(</code><code>"Password1"</code><code>,</code><code>false</code><code>);</code>
<code> </code><code>MemberName=</code><code>"Password1"</code><code>,</code>
<code> </code><code>Message= </code><code>"請輸入登入密碼,登入密碼不能為空!"</code>
<code> </code><code>case</code><code>"Password2"</code><code>:</code>
<code> </code><code>ControllerContext.Controller.TempData.Add(</code><code>"Password2"</code><code>,</code><code>false</code><code>);</code>
<code> </code><code>MemberName=</code><code>"Pssword2"</code><code>,</code>
<code> </code><code>Message= </code><code>"請再次輸入密碼,密碼不能為空!"</code>
<code> </code><code>case</code><code>"Name"</code><code>:</code>
<code> </code><code>returnEnumerable.Empty<ModelValidationResult>();</code>
<code> </code><code>}</code>
<code> </code><code>returnEnumerable.Empty<ModelValidationResult>();</code>
<code> </code><code>}</code>
首先我們在代碼1-3中定義了個MyCustomModelPropertyValidator類型,并且定義了構造函數這點非常重要(看到Model驗證提供程式部分就明白了),在Validate()方法中對一些屬性進行了基本的的驗證,并且依據方法所限制的傳回類型去傳回,對于代碼的實作部分,看過上篇的朋友就知道了在預設的Model綁定器執行Model屬性綁定的時候會進行Model屬性驗證,會在周遊中循環的調用這個MyCustomModelPropertyValidator類型的驗證方法,這裡有不明白的朋友可以去看一下上篇。
我們再來看第二部分,第二部分的主要驗證是驗證兩次輸入的密碼是否是相同的,來看示例代碼1-4。
代碼1-4
<code> </code><code>public</code> <code>class</code> <code>MyCustomModelPassWordValidator : ModelValidator</code>
<code> </code><code>publicMyCustomModelPassWordValidator(ModelMetadatamodelMetadata, ControllerContextcontrollerContext)</code>
<code> </code><code>: </code><code>base</code><code>(modelMetadata,controllerContext)</code>
<code> </code><code>Models.RegistrationInformationregInfo = Metadata.Model </code><code>as</code> <code>Models.RegistrationInformation;</code>
<code> </code><code>if</code><code>(regInfo != </code><code>null</code><code>)</code>
<code> </code><code>if</code><code>(!ControllerContext.Controller.TempData.ContainsKey(</code><code>"Password1"</code><code>)</code>
<code> </code><code>&&!ControllerContext.Controller.TempData.ContainsKey(</code><code>"Password2"</code><code>))</code>
<code> </code><code>{</code>
<code> </code><code>if</code><code>(regInfo.Password1 != regInfo.Password2)</code>
<code> </code><code>{</code>
<code> </code><code>new</code> <code>ModelValidationResult()</code>
<code> </code><code>{</code>
<code> </code><code>MemberName=</code><code>"Password2"</code><code>,</code>
<code> </code><code>Message=</code><code>"請重新輸入密碼,與上次輸入密碼不同!"</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>}</code>
在代碼1-4中,有細心的朋友發現了跟代碼1-3的實作方式有很大的差別,對的因為我把這個MyCustomModelPassWordValidator類型定義為Model級驗證了,MyCustomModelPassWordValidator類型所被調用的位置是在預設Model綁定器對Model屬性驗證完畢并且進行綁定了過後,再在預設Model綁定器的OnModelUpdated()方法中調用的,是以上述可以直接通過Metadata的Model屬性直接擷取Model的值,由此來進行驗證,而這句ControllerContext.Controller.TempData.ContainsKey("Password1")代碼的含義是判斷在Model的Password1屬性在屬性級驗證的時候是否通過了,因為每個Model驗證器之間無法傳遞消息,隻有ControllerContext是指向一個引用的,所有用它來做資料資訊的承載。在代碼1-3中已經定義了。(對于第二部分的了解不太明白的朋友可以結合上篇和本篇來看)
現在我們再看第三部分,意思和第二部分一樣,示例代碼1-5。
代碼1-5
<code> </code><code>public</code> <code>class</code> <code>MyCustomModelNameValidator : ModelValidator</code>
<code> </code><code>publicMyCustomModelNameValidator(ModelMetadatamodelMetadata, ControllerContextcontrollerConttext)</code>
<code> </code><code>: </code><code>base</code><code>(modelMetadata,controllerConttext)</code>
<code> </code><code>if</code><code>(</code><code>string</code><code>.Compare(regInfo.Name, </code><code>"jinyuan"</code><code>, </code><code>true</code><code>)== 0)</code>
<code> </code><code>returnnew ModelValidationResult[]</code>
<code> </code><code>new</code> <code>ModelValidationResult()</code>
<code> </code><code>MemberName=</code><code>""</code><code>,</code>
<code> </code><code>Message=</code><code>"您輸入的名稱違法了,立即更改不然查水表"</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
代碼1-5的功能就是在Model級驗證時對Model的Name屬性進行驗證。
ModelValidatorProvider
上個小節定義了三個部分,第一部分為Model屬性級驗證,二三部分皆為Model級驗證,然而在預設的Model綁定器(DefaultModelBinder類型)執行的時候,它才不管呢,它隻負責從ModelValidatorProviders.Providers中擷取我們自定義的Model驗證器提供程式(ModelValidatorProvider類型執行個體),再由Model驗證器提供程式給它提供Model驗證器,這個時候就需要我們的自定義的Model驗證器提供程式具有判斷我們自身是在哪裡被調用的,并且傳回對應的Model驗證器,來看示例代碼1-6。
代碼1-6
<code>namespace</code> <code>MvcApplication.ModelValidators</code>
<code>{</code>
<code> </code><code>public</code> <code>class</code> <code>MyCustomModelValidatorProvider:ModelValidatorProvider</code>
<code> </code><code>public</code> <code>override</code> <code>IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContextcontext)</code>
<code> </code><code>if</code><code>(metadata.ContainerType == </code><code>typeof</code><code>(Models.RegistrationInformation))</code>
<code> </code><code>returnnew ModelValidator[]</code>
<code> </code><code>newMyCustomModelPropertyValidator(metadata,context)</code>
<code> </code><code>};</code>
<code> </code><code>elseif (metadata.ModelType == </code><code>typeof</code><code>(Models.RegistrationInformation))</code>
<code> </code><code>new</code> <code>MyCustomModelPassWordValidator(metadata,context),</code>
<code> </code><code>newMyCustomModelNameValidator(metadata,context)</code>
<code> </code><code>returnEnumerable.Empty<ModelValidator>();</code>
<code>}</code>
在代碼1-6中,我們定義了個自定義的Model驗證器提供程式MyCustomModelValidatorProvider類型,在MyCustomModelValidatorProvider類型中是根據GetValidators()方法的參數metadata所包含的資訊來判斷自己被預設的Model綁定器(DefaultModelBinder類型)調用的位置。(對于Model中繼資料部分大家可檢視我前面跟大家分享的Model中繼資料系列)
到了這裡幾乎就已經全部完成了,現在注冊到系統中是不能使用的,有的朋友會罵了,不能用瞎扯蛋扯半天,大家莫急,聽我解釋,是因為我們自定義的Model驗證器和Model驗證器提供程式都要受到MVC架構的預設的Model綁定器(DefaultModelBinder類型)調用的限制,是必須按照它方法内部定義的來往下寫,在這裡我們不圖省事,再定義一個自定義實作DefaultModelBinder類型的Model綁定器,來看示例代碼1-7。
代碼1-7
<code> </code><code>public</code> <code>class</code> <code>MyCustomModelValidatorDefaultModelBinder : DefaultModelBinder</code>
<code> </code><code>protectedoverride voidSetProperty(ControllerContextcontrollerContext, ModelBindingContextbindingContext, PropertyDescriptorpropertyDescriptor, </code><code>object</code> <code>value)</code>
<code> </code><code>base</code><code>.SetProperty(controllerContext,bindingContext, propertyDescriptor, value);</code>
<code> </code><code>foreach</code><code>(ModelValidator modelValidator </code><code>in</code> <code>ModelValidatorProviders.Providers.GetValidators(bindingContext.PropertyMetadata[propertyDescriptor.Name],controllerContext))</code>
<code> </code><code>IEnumerable<ModelValidationResult> modelValidationResults =modelValidator.Validate(value);</code>
<code> </code><code>foreach</code><code>(ModelValidationResult modelValidationResultin modelValidationResults)</code>
<code> </code><code>bindingContext.ModelState.AddModelError(modelValidationResult.MemberName,modelValidationResult.Message);</code>
<code> </code><code>protectedoverride voidOnModelUpdated(ControllerContextcontrollerContext, ModelBindingContextbindingContext)</code>
<code> </code><code>//base.OnModelUpdated(controllerContext,bindingContext);</code>
<code> </code><code>foreach</code><code>(ModelValidator modelValidator </code><code>in</code> <code>ModelValidatorProviders.Providers.GetValidators(bindingContext.ModelMetadata,controllerContext))</code>
<code> </code><code>IEnumerable<ModelValidationResult> modelValidationResults =modelValidator.Validate(</code><code>null</code><code>);</code>
<code> </code><code>bindingContext.ModelState.AddModelError(modelValidationResult.MemberName,modelValidationResult.Message);</code>
有了代碼1-7中的Model綁定器就可以吻合我們前面所定義的Model驗證器以及提供程式了。
我要把MyCustomModelValidatorProvider類型和MyCustomModelValidatorDefaultModelBinder類型注冊到系統中,将示例代碼1-8部分添加至Global.asax檔案的Application_Start()方法中。
代碼1-8
<code>ModelBinders.Binders.Add(</code><code>typeof</code><code>(Models.RegistrationInformation), newBinders.MyCustomModelValidatorDefaultModelBinder());</code>
<code>ModelValidatorProviders.Providers.Insert(0, newModelValidators.MyCustomModelValidatorProvider());</code>
看下圖1,圖1為初始的展示頁面,圖2為圖1中點選送出後執行驗證過後。
圖1
<a href="http://s3.51cto.com/wyfs02/M02/3D/23/wKiom1PD0VXzie6XAAEJrDFD7Mw021.jpg" target="_blank"></a>
圖2
<a href="http://s3.51cto.com/wyfs02/M00/3D/23/wKioL1PD0TfzgIHTAAHA1OkfjUQ569.jpg" target="_blank"></a>
本文轉自jinyuan0829 51CTO部落格,原文連結:http://blog.51cto.com/jinyuan/1438118,如需轉載請自行聯系原作者