ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidatorProvider
在《ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidator》中我們介紹了ASP.NET MVC用于Model驗證的四種ModelValidator,那么這些ModelValidator是如何被創(chuàng)建的呢?ASP.NET MVC的很多組件(比如ModelBinder和Filter)都采用了基于Provider的提供機制,這篇文章為你講述這些ModelValidator對應(yīng)的ModelValidatorProvider。[本文已經(jīng)同步到《How ASP.NET MVC Works?》中]
目錄
一、ModelValidatorProvider
二、DataAnnotationsModelValidator
三、ClientDataTypeModelValidatorProvider
四、DataErrorInfoModelValidatorProvider
一、ModelValidatorProvider
我們通過注冊ModelValidatorProvider來創(chuàng)建相應(yīng)的ModelValidator,所有的ModelValidatorProvider直接或者間接地繼承類型ModelValidatorProvider。如下面的代碼片斷所示,ModelValidator的提供實現(xiàn)在抽象方法GetValidators種,返回的是一個ModelValidator集合。
1: public abstract class ModelValidatorProvider
2: {
3: public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ModelBindingExecutionContext context);
4: }
由于ValueProvider提供的數(shù)據(jù)值僅限于簡單類型,所以針對復雜類型的Model綁定采用一個遞歸的過程對作為Model對象的所有屬性進行綁定。Model驗證可以看成是Model綁定的后續(xù)環(huán)節(jié),它對綁定的數(shù)據(jù)實施驗證,所以Model驗證也是一個遞歸的過程,它采用基于屬性的驗證規(guī)則對綁定的屬性值實施驗證。GetValidators方法具有兩個參數(shù),類型ModelMetadata的metadata參數(shù)用于或者相應(yīng)的驗證規(guī)則,而參數(shù)context則是表示當前Model綁定上下文的ModelBindingExecutionContext對象。
二、DataAnnotationsModelValidator
上面我們提到過的針對數(shù)據(jù)標注特性驗證方式的DataAnnotationsModelValidator對應(yīng)的ModelValidatorProvider類型為DataAnnotationsModelValidatorProvider。如下面的代碼片斷所示,DataAnnotationsModelValidatorProvider繼承自另一個抽象類型AssociatedValidatorProvider。
1: public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider
2: {
3: //其他成員
4: public DataAnnotationsModelValidatorProvider();
5: protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context,IEnumerable<Attribute> attributes);
6: }
類型名稱AssociatedValidatorProvider中所謂的“關(guān)聯(lián)(Association)”實際上代表的是關(guān)聯(lián)的特性列表,即它根據(jù)從Model元數(shù)據(jù)中得到的用于定義驗證規(guī)則的特性列表來提供相應(yīng)的ModelValidator。如下面的代碼片斷所示,AssociatedValidatorProvider定義一個受保護的虛方法GetTypeDescriptor用于獲取指定類型的描述對象(其類型實現(xiàn)了接口ICustomTypeDescriptor)。被解析出來的關(guān)聯(lián)特性最終傳入抽象的GetValidators方法實現(xiàn)了對ModelValidator的提供,而DataAnnotationsModelValidatorProvider正是實現(xiàn)了這個方法來創(chuàng)建相應(yīng)的DataAnnotationsModelValidator列表。
1: public abstract class AssociatedValidatorProvider : ModelValidatorProvider
2: {
3: protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type);
4: public sealed override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
5: protected abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes);
6: }
在被重寫的GetValidators方法中,如果當前Model元數(shù)據(jù)是基于某個屬性的(表示容器類型的ContainerType不會Null并且具有屬性名稱),在調(diào)用GetTypeDescriptor方法獲取容器類型描述對象,進而根據(jù)屬性類型得到用于描述屬性的PropertyDescriptor對象,最終通過該描述對象得到應(yīng)用在對應(yīng)屬性上的所有特性并調(diào)用抽象方法GetValidators返回基于屬性的ModelValidator列表。對于非屬性Model元數(shù)據(jù),在直接調(diào)用GetTypeDescriptor方法得到Model類型描述對象,進而獲取應(yīng)用在Model類型上的所有特性并傳入抽象方法GetValidators實現(xiàn)對針對Model類型的ModelValidator的提供。
三、ClientDataTypeModelValidatorProvider
針對數(shù)值和日期類型客戶端驗證的NumericModelValidator和DateModelValidator最終是通過具有如下定義的ClientDataTypeModelValidatorProvider來提供的。在GetValidators方法中,它會根據(jù)指定的Model元數(shù)據(jù)判斷是否屬于數(shù)值類型/DateTime類型,如果是則直接返回一個包含單個NumericModelValidator/DateModelValidator對象的ModelValidator集合。在這里被視為數(shù)值的數(shù)據(jù)類型包括byte,、sbyte、short,、ushort、int、uint,long,、ulong、float、double,和decimal等。
1: public class ClientDataTypeModelValidatorProvider : ModelValidatorProvider
2: {
3: public ClientDataTypeModelValidatorProvider();
4: public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
5: }
四、DataErrorInfoModelValidatorProvider
兩個具體的DataErrorInfoModelValidator,即DataErrorInfoClassModelValidator和DataErrorInfoPropertyModelValidator最終是通過具有如下定義的DataErrorInfoModelValidatorProvider來提供的。對于GetValidators的具體實現(xiàn)來說,如果Model類型實現(xiàn)了IDataErrorInfo接口,會基于制定的Model元數(shù)據(jù)和Controller上下文創(chuàng)建一個DataErrorInfoClassModelValidator對象置于返回的ModelValidtor集合中。對于基于屬性的Model元數(shù)據(jù)來說,如果其容器類型實現(xiàn)了IDataErrorInfo接口,該方法返回的ModelValidtor集合中還會包含一個基于指定Model元數(shù)據(jù)和Controller上下文創(chuàng)建的DataErrorInfoPropertyModelValidator對象。
1: public class DataErrorInfoModelValidatorProvider : ModelValidatorProvider
2: {
3: public DataErrorInfoModelValidatorProvider();
4: public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
5: }
為了讓讀者更好地了解DataErrorInfoModelValidator的驗證規(guī)則,以及定義在DataErrorInfoPropertyModelValidator中針對它的提供機制,我們來演示一個簡單的實例。在通過Visual Studio的ASP.NET MVC項目模板創(chuàng)建的空Web應(yīng)用中我們定義了如下一個實現(xiàn)了IDataErrorInfo接口的Contact類型。
1: public class Contact: IDataErrorInfo
2: {
3: public string Error
4: {
5: get { return "無效聯(lián)系人!";}
6: }
7: public string this[string columnName]
8: {
9: get
10: {
11: switch (columnName)
12: {
13: case "Name" : return "姓名是必需的!";
14: case "PhoneNo" : return "電話號碼格式錯誤!";
15: case "EmailAdderss" : return "無效的電子郵箱地址!";
16: default : return null;
17: }
18: }
19: }
20: public string Name { get; set; }
21: public string PhoneNo { get; set; }
22: public string EmailAdderss { get; set; }
23: }
然后創(chuàng)建了如下一個默認的HomeController類。在Action方法Index中,我們通過DataErrorInfoModelValidatorProvider根據(jù)Contact類型極其屬性的Model元數(shù)據(jù)創(chuàng)建了一個ModelValidator列表,然后使用這個列表中的每個具體的ModelValidator對一個Contact對象實施驗證,并將ModelValidator的類型和作為驗證結(jié)果的ModelValidationResult對象的ErrorMessage屬性呈現(xiàn)出來。
1: public class HomeController : Controller
2: {
3: public void Index()
4: {
5: Contact contact = new Contact();
6: ModelValidatorProvider validatorProvider = new DataErrorInfoModelValidatorProvider();
7: foreach (var validator in GetValidators(contact, validatorProvider))
8: {
9: var validationResults = validator.Validate(contact);
10: if (validationResults.Any())
11: {
12: Response.Write(validator.GetType().Name + "<br/>");
13: }
14: foreach(var validationResult in validationResults)
15: {
16: Response.Write(validationResult.Message + "<br>");
17: }
18: }
19: }
20:
21: private IEnumerable<ModelValidator> GetValidators(Contact contact, ModelValidatorProvider validatorProvider)
22: {
23: ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => contact, typeof(Contact));
24: foreach (var validator in validatorProvider.GetValidators(metadata, ControllerContext))
25: {
26: yield return validator;
27: }
28:
29: foreach (var propertyMetadata in metadata.Properties)
30: {
31: foreach (var validator in validatorProvider.GetValidators(propertyMetadata, ControllerContext))
32: {
33: yield return validator;
34: }
35: }
36: }
37: }
上面的程序運行之后會在瀏覽器中呈現(xiàn)出如下所示的輸出結(jié)果,從中可以看到針對Contact類型的Model元數(shù)據(jù)創(chuàng)建的是一個DataErrorInfoClassModelValidator,而針對其屬性的Model元數(shù)據(jù)創(chuàng)建的則是一個DataErrorInfoPropertyModelValidator對象。前者對Contact對象本身實施驗證,并將Error屬性作為驗證結(jié)果的錯誤消息;后者針對應(yīng)的屬性實施驗證,驗證結(jié)果的錯誤消息來源于將屬性名稱作為索引的值。
1: DataErrorInfoClassModelValidator
2: 無效聯(lián)系人!
3: DataErrorInfoPropertyModelValidator
4: 姓名是必需的!
5: DataErrorInfoPropertyModelValidator
6: 電話號碼格式錯誤!
7: DataErrorInfoPropertyModelValidator
8: 無效的電子郵箱地址!
ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidator
ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidatorProvider
ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidatorProviders


在《ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidator》中我們介紹了ASP.NET MVC用于Model驗證的四種ModelValidator,那么這些ModelValidator是如何被創(chuàng)建的呢?ASP.NET MVC的很多組件(比如ModelBinder和Filter)都采用了基于Provider的提供機制,這篇文章為你講述這些ModelValidator對應(yīng)的ModelValidatorProvider。
浙公網(wǎng)安備 33010602011771號