public abstract class ModelValidator { public virtual bool IsRequired { get { return false; } } public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules()//用于客户端验证 { return Enumerable.Empty<ModelClientValidationRule>(); } public abstract IEnumerable<ModelValidationResult> Validate(object container);//用于服务端验证 public static ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context) { return new CompositeModelValidator(metadata, context); } }
ModelValidationResult集合只有在验证失败的情况下才会返回。验证通过的情况下,返回值时空集合
public class ModelValidationResult { private string _memberName; private string _message; public string MemberName { get { return _memberName ?? String.Empty; } set { _memberName = value; } } public string Message { get { return _message ?? String.Empty; } set { _message = value; } } }
public class ModelClientValidationRule { private readonly Dictionary<string, object> _validationParameters = new Dictionary<string, object>(); private string _validationType; public string ErrorMessage { get; set; } public IDictionary<string, object> ValidationParameters { get { return _validationParameters; } } public string ValidationType { get { return _validationType ?? String.Empty; } set { _validationType = value; } } }
当CompositeModelValidator 被用于验证一个容器对象的时候,会先验证其属性成员。针对容器对象自身的验证只有在所有属性值都通过验证的情况下才会进行
private class CompositeModelValidator : ModelValidator { public CompositeModelValidator(ModelMetadata metadata, ControllerContext controllerContext) : base(metadata, controllerContext) { } public override IEnumerable<ModelValidationResult> Validate(object container) { bool propertiesValid = true; foreach (ModelMetadata propertyMetadata in Metadata.Properties) { foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(ControllerContext)) { foreach (ModelValidationResult propertyResult in propertyValidator.Validate(Metadata.Model)) { propertiesValid = false; yield return new ModelValidationResult { MemberName = DefaultModelBinder.CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName), Message = propertyResult.Message }; } } } if (propertiesValid) { foreach (ModelValidator typeValidator in Metadata.GetValidators(ControllerContext)) { foreach (ModelValidationResult typeResult in typeValidator.Validate(container)) { yield return typeResult; } } } } }
public class DataAnnotationsModelValidator : ModelValidator后边会有详细的介绍
NurnericModelValidator和DateModelValidator是其继承者
public interface IDataErrorInfo { string Error { get; }//基于自身的错误消息 string this[string columnName] { get; }//数据成员的错误消息 }
ASPNET MVC多采用Provider的方式提供组件.
public abstract class ModelValidatorProvider {//只有这一个方法 public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context); }
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProviderpublic abstract class AssociatedValidatorProvider : ModelValidatorProvider { protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) { return TypeDescriptorHelper.Get(type); } public sealed override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) { if (metadata == null) { throw new ArgumentNullException("metadata"); } if (context == null) { throw new ArgumentNullException("context"); } if (metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName)) { return GetValidatorsForProperty(metadata, context); } return GetValidatorsForType(metadata, context); } protected abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes); private IEnumerable<ModelValidator> GetValidatorsForProperty(ModelMetadata metadata, ControllerContext context) {//获取在父容器中的属性上的特性和数据类型上 的特性的列表. ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(metadata.ContainerType); PropertyDescriptor property = typeDescriptor.GetProperties().Find(metadata.PropertyName, true); if (property == null) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.Common_PropertyNotFound, metadata.ContainerType.FullName, metadata.PropertyName), "metadata"); } return GetValidators(metadata, context, property.Attributes.OfType<Attribute>()); } private IEnumerable<ModelValidator> GetValidatorsForType(ModelMetadata metadata, ControllerContext context) {//获取数据类型上的特性列表 return GetValidators(metadata, context, GetTypeDescriptor(metadata.ModelType).GetAttributes().Cast<Attribute>()); } }
针对数字/日期类型客户端验证的NumericModelValidator 和DateModelValidator 最终是通过具有如下定义的System.Web.Mvc.ClientDataTypeModelValidatorProvider 来提供的。在实现的GetValidators 方法中,它会根据指定ModelMetadata 判断被验证类型是否属于数字/DateTime 类型,如果是则直接返回一个包含单个NumericModelValidator 或者DateModelValidator对象的ModelValidator 集合。在这里被视为数字的类型包括byte 、sbyte 、short 、ushort 、int 、uint 、long 、ulong 、float 、double 和decimal 等。
public class ClientDataTypeModelValidatorProvider : ModelValidatorProvider { private static readonly HashSet<Type> _numericTypes = new HashSet<Type>(new Type[] { typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); //判断数据类型,返回Date或者Num的Validator private static IEnumerable<ModelValidator> GetValidatorsImpl(ModelMetadata metadata, ControllerContext context) { Type type = metadata.ModelType; if (IsDateTimeType(type, metadata)) { yield return new DateModelValidator(metadata, context); } if (IsNumericType(type)) { yield return new NumericModelValidator(metadata, context); } } }
旨在对实现了IDataErrorInfo 接口的数据实施验证的两个DataErrorI nfoModelValidator(即DataErrorI nfoClassModelValidator 和DataErrorI nfoPrope均rModelValidator) ,最终是通过具有如下定义的System. Web.Mvc.DataErrorInfoModelValidatorProvider 来提供的。在实现的Get Validators 方法中,如果被验证数据类型实现了IDat aErrorI nfo 接口,它会基于指定的ModelMetadata 和ControllerContext 创建一个DataErrorInfoClassModelValidator 对象置于返回的ModelValidator 集合中。
如果被验证的对象是容器对象的某个属性,并且容器对象的类型(不是属性类型)实现了IDataE rrorInfo 接口,该方法返回的ModelValidator 集合中还会包含一个基于指定Mode lM etadata 和ControllerContext 创建的DataEηorI nfoPrope吗rModelValidator 对象。
private static IEnumerable<ModelValidator> GetValidatorsImpl(ModelMetadata metadata, ControllerContext context) { // If the metadata describes a model that implements IDataErrorInfo, we should call its // Error property at the appropriate time. if (TypeImplementsIDataErrorInfo(metadata.ModelType)) { yield return new DataErrorInfoClassModelValidator(metadata, context); } // If the metadata describes a property of a container that implements IDataErrorInfo, // we should call its Item indexer at the appropriate time. if (TypeImplementsIDataErrorInfo(metadata.ContainerType)) { yield return new DataErrorInfoPropertyModelValidator(metadata, context); } }
ModelMetadata有一个方法,也可以获取ModelValidator.逻辑也是调用Provider获取的
public virtual IEnumerable<ModelValidator> GetValidators(ControllerContext context) { return ModelValidatorProviders.Providers.GetValidators(this, context); } public static class ModelValidatorProviders { private static readonly ModelValidatorProviderCollection _providers = new ModelValidatorProviderCollection() { new DataAnnotationsModelValidatorProvider(), new DataErrorInfoModelValidatorProvider(), new ClientDataTypeModelValidatorProvider() }; public static ModelValidatorProviderCollection Providers { get { return _providers; } } } public class ModelValidatorProviderCollection : Collection<ModelValidatorProvider> { public IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {//集合中每个ModelValidatorProvider 创建的ModelValidator 会包含在该列表中。 return CombinedItems.SelectMany(provider => provider.GetValidators(metadata, context)); } }
public class ModelState { private ModelErrorCollection _errors = new ModelErrorCollection(); //数据验证的结果,错误的集合 public ModelErrorCollection Errors { get { return this._errors; } } //ValueProvider提供的值 public ValueProviderResult Value { get; set; } }
if (value == null && bindingContext.ModelState.IsValidField(modelStateKey)) { ModelValidator requiredValidator = ModelValidatorProviders.Providers.GetValidators(propertyMetadata, controllerContext).Where(v => v.IsRequired).FirstOrDefault(); if (requiredValidator != null) { foreach (ModelValidationResult validationResult in requiredValidator.Validate(bindingContext.Model)) { bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message); } } }
ValidationSummary 方法通过判断ModelStateDictionary的Key 是否为空来判断ModelState 是否针对一个属性
internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) { // need to replace the property filter + model object and create an inner binding context ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model); // validation if (OnModelUpdating(controllerContext, newBindingContext)) { BindProperties(controllerContext, newBindingContext); OnModelUpdated(controllerContext, newBindingContext); } } protected virtual void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { Dictionary<string, bool> startedValid = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(bindingContext.ModelMetadata, controllerContext).Validate(null)) { string subPropertyName = CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName); if (!startedValid.ContainsKey(subPropertyName)) { startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName); } if (startedValid[subPropertyName]) { bindingContext.ModelState.AddModelError(subPropertyName, validationResult.Message); } } }
继承的时候,需要重写IsValid(,)方法.FormatErrorMessage会格式化错误消息,GetValidationResult会调用IsValid方法得到Result
public class DataAnnotationsModelValidator : ModelValidator { public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute) { } public override IEnumerable<ModelValidationResult> Validate(object container) { // Per the WCF RIA Services team, instance can never be null (if you have // no parent, you pass yourself for the "instance" parameter). ValidationContext context = new ValidationContext(container ?? Metadata.Model, null, null); context.DisplayName = Metadata.GetDisplayName(); ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context); if (result != ValidationResult.Success) { yield return new ModelValidationResult { Message = result.ErrorMessage }; } } }
DataAnnotationsModelValidator<TAttribute>:DataAnnotationsModelValidator where TAttribute: ValidationAttribute public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator where TAttribute : ValidationAttribute { public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext context, TAttribute attribute) : base(metadata, context, attribute) { } protected new TAttribute Attribute { get { return (TAttribute)base.Attribute; } } }
public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute> { public RequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute) : base(metadata, context, attribute) { } public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { return new[] { new ModelClientValidationRequiredRule(ErrorMessage) }; } }
委托DataAnnotationsModelValidationFactory 根据ModelMetadata、ControllerContext 和ValidationAttribute 创建一个ModelValidator 对象。字段AttributeFactories 表示的字典将验证特性的类型作为Key ,换句话说它维护一个ValidationAttribute 特性类型和对应ModelValidator工厂的匹配关系。
在重写的GetValidators 方法中,针对提供的每一个ValidationAttribute 特性,它先根据其类型从AttributeFactories 字典中获取一个对应的DataAnn otationsModelValidationFactory 委托。如果该委托对象存在,则用它来创建相应的ModelValidator 对象,否则就采用字段DefaultAttributeFactory 表示的DataAnnotationsModelValidationFactory 委托来进行ModelValidator 的创建。
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider { internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory = (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute); internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = BuildAttributeFactoriesDictionary(); private static Dictionary<Type, DataAnnotationsModelValidationFactory> BuildAttributeFactoriesDictionary() { var dict = new Dictionary<Type, DataAnnotationsModelValidationFactory>(); AddValidationAttributeAdapter(dict, typeof(RangeAttribute), (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)); AddValidationAttributeAdapter(dict, typeof(RegularExpressionAttribute), (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)); AddValidationAttributeAdapter(dict, typeof(RequiredAttribute), (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)); AddValidationAttributeAdapter(dict, typeof(StringLengthAttribute), (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)); AddValidationAttributeAdapter(dict, ValidationAttributeHelpers.MembershipPasswordAttributeType, (metadata, context, attribute) => new MembershipPasswordAttributeAdapter(metadata, context, attribute)); AddValidationAttributeAdapter(dict, ValidationAttributeHelpers.CompareAttributeType, (metadata, context, attribute) => new CompareAttributeAdapter(metadata, context, attribute)); AddValidationAttributeAdapter(dict, ValidationAttributeHelpers.FileExtensionsAttributeType, (metadata, context, attribute) => new FileExtensionsAttributeAdapter(metadata, context, attribute)); AddDataTypeAttributeAdapter(dict, ValidationAttributeHelpers.CreditCardAttributeType, "creditcard"); AddDataTypeAttributeAdapter(dict, ValidationAttributeHelpers.EmailAddressAttributeType, "email"); AddDataTypeAttributeAdapter(dict, ValidationAttributeHelpers.PhoneAttributeType, "phone"); AddDataTypeAttributeAdapter(dict, ValidationAttributeHelpers.UrlAttributeType, "url"); return dict; } private static void AddValidationAttributeAdapter(Dictionary<Type, DataAnnotationsModelValidationFactory> dictionary, Type validataionAttributeType, DataAnnotationsModelValidationFactory factory) { Contract.Assert(dictionary != null); if (validataionAttributeType != null) { dictionary.Add(validataionAttributeType, factory); } } private static void AddDataTypeAttributeAdapter(Dictionary<Type, DataAnnotationsModelValidationFactory> dictionary, Type attributeType, string ruleName) { AddValidationAttributeAdapter( dictionary, attributeType, (metadata, context, attribute) => new DataTypeAttributeAdapter(metadata, context, (DataTypeAttribute)attribute, ruleName)); } }
除了AttributeFactories 和DefaultAttributeFactory , DataAnnotationsModelValidatorProvider还具有DefaultValidatableFactory 和ValidatableFactories 两个静态字段,它们用于针对可验证对象(实现了IValidatableObject 接口)的ModelValidator 创建。字段DefaultValidatableFactory的类型是另外一个名为DataAnnotations Validatab leObjectAdapterFactory 的委托,该委托根据
ModelMetadata 和ControllerContext 创建相应的ModelValidator 。字段Validatab leF actories 是一个以此委托为Value 、以Type 对象为Key 的字典。
当DataAnnotationsModelValidatorProvider 完成了针对基于验证特性的ModelValidator 的创建之后,如果被验证数据类型实现了IValidatableObject 接口,它会先从静态字段ValidatableFactories 中根据此类型获取一个对应的DataAnnotationsValidatableObjec tAdapterFactory委托。如果匹配的委托对象存在,则用其进行ModelValidator 的创建,否则采用字段DefaultValidatableFactory 表示的默认工厂来创建相应的ModelValidator 对象。创建的是 public class ValidatableObjectAdapter : ModelValidator.
可以根据需要对Adapter和AdapterFactory进行注册.
可以通过扩展,使用对参数添加的验证特性.默认的情况下,是不会读取应用在参数上的验证特性的.
DefaultModelBinder对简单参数的绑定,是不会进行参数验证的.只对复杂的参数类型进行验证.可以自定义继承者DefaultModelBinder的类,实现对简单类型的验证.添加对简单类型验证的代码
ParameterDescription包含了应用在参数上的特性
默认的数据验证Provider,是根据参数的数据类型生成ModelValidator的,没有读取参数上的特性.因此可以自定义继承自DataAnnotationsModelValidatorProvider的Provider,读取参数上的特性,创建ModelValidator,添加到默认的列表中.
想办法将ParameterDescription的信息传递到Provider中,因此可以将信息放入ControllerContext中传递过去.自定义ActionInvoke,将信息放入ControllerContext中
略…
一种Model类型,多种验证规则
客户端验证
JQuery验证
基于JQuery的Model验证
Data-val表示对用户的输入值验证
Data-val-name name表示JQuery验证规则 的名称
Data-val-name-para表示可选的验证参数
ModelClientValidationRule表示客户端验证规则
ModelValidator中有一个方法GetClientValidationRules返回客户端验证规则
客户端验证在这里还涉及到一个重要的接口System. Web.Mvc.IClientValidatable ,它具有唯一的GetClientValidationRules 方法来返回一个以System.Web.Mvc.ModelClientValidationRule对象表示的客户端验证规则列表。所有支持客户端验证的ValidationAttribute 都需要实现IClientValidatable 接口并通过实现.DataAnnotationsModelValidator.GetClientValidationRules 方法,如果对应的ValidationA伽ibute 实现了IClientValidatable 接口,它( ValidationAttribute )的GetClientValidationRules 方法被调用并将返回的ModelClientValidationRule 列表作为该方法的返回值。
原文:http://www.cnblogs.com/zhangliming/p/4567270.html