天天看點

ASP.NETMVC Model驗證(四)

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&lt;ModelClientValidationRule&gt;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&lt;ModelValidationResult&gt; 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&lt;ModelValidationResult&gt; 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&lt;ModelValidationResult&gt;();</code>

<code>            </code><code>}</code>

<code>            </code><code>returnEnumerable.Empty&lt;ModelValidationResult&gt;();</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>&amp;&amp;!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&lt;ModelValidator&gt; 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&lt;ModelValidator&gt;();</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&lt;ModelValidationResult&gt; 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&lt;ModelValidationResult&gt; 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,如需轉載請自行聯系原作者

繼續閱讀