码迷,mamicode.com
首页 > Web开发 > 详细

asp.net mvc源码分析-ModelValidatorProviders 客户端的验证

时间:2016-04-02 12:15:14      阅读:260      评论:0      收藏:0      [点我收藏+]

标签:

几年写过asp.net mvc源码分析-ModelValidatorProviders 当时主要是考虑mvc的流程对,客户端的验证也只是简单的提及了一下,现在我们来仔细看一下客户端的验证。

技术分享

如图所示, 首先我们要知道这里的data-val这些属性是在哪里生成的?可以肯定是在mvc后台生成的,

@Html.PasswordFor(m => m.Password) 生成input
@Html.ValidationMessageFor(m => m.Password) 生成span

调用层级关系:

InputExtensions:

public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
return PasswordFor(htmlHelper, expression, null /* htmlAttributes */);
}

调用

public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
if (expression == null) {
throw new ArgumentNullException("expression");
}

return PasswordHelper(htmlHelper,
ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
ExpressionHelper.GetExpressionText(expression),
null /* value */,
htmlAttributes);
}

调用

private static MvcHtmlString PasswordHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string name, object value, IDictionary<string, object> htmlAttributes) {
return InputHelper(htmlHelper, InputType.Password, metadata, name, value, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
}

再调用

  private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, IDictionary<string, object> htmlAttributes) {
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            if (String.IsNullOrEmpty(fullName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
            }

            TagBuilder tagBuilder = new TagBuilder("input");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
            tagBuilder.MergeAttribute("name", fullName, true);

            string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);
            bool usedModelState = false;

            switch (inputType) {
                case InputType.CheckBox:
                    bool? modelStateWasChecked = htmlHelper.GetModelStateValue(fullName, typeof(bool)) as bool?;
                    if (modelStateWasChecked.HasValue) {
                        isChecked = modelStateWasChecked.Value;
                        usedModelState = true;
                    }
                    goto case InputType.Radio;
                case InputType.Radio:
                    if (!usedModelState) {
                        string modelStateValue = htmlHelper.GetModelStateValue(fullName, typeof(string)) as string;
                        if (modelStateValue != null) {
                            isChecked = String.Equals(modelStateValue, valueParameter, StringComparison.Ordinal);
                            usedModelState = true;
                        }
                    }
                    if (!usedModelState && useViewData) {
                        isChecked = htmlHelper.EvalBoolean(fullName);
                    }
                    if (isChecked) {
                        tagBuilder.MergeAttribute("checked", "checked");
                    }
                    tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
                    break;
                case InputType.Password:
                    if (value != null) {
                        tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
                    }
                    break;
                default:
                    string attemptedValue = (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
                    tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(fullName) : valueParameter), isExplicitValue);
                    break;
            }

            if (setId) {
                tagBuilder.GenerateId(fullName);
            }

            // If there are any errors for a named field, we add the css attribute.
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState)) {
                if (modelState.Errors.Count > 0) {
                    tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
                }
            }

            tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));

            if (inputType == InputType.CheckBox) {
                // Render an additional <input type="hidden".../> for checkboxes. This
                // addresses scenarios where unchecked checkboxes are not sent in the request.
                // Sending a hidden input makes it possible to know that the checkbox was present
                // on the page when the request was submitted.
                StringBuilder inputItemBuilder = new StringBuilder();
                inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));

                TagBuilder hiddenInput = new TagBuilder("input");
                hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
                hiddenInput.MergeAttribute("name", fullName);
                hiddenInput.MergeAttribute("value", "false");
                inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
                return MvcHtmlString.Create(inputItemBuilder.ToString());
            }

            return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
        }

这个方法有个  TagBuilder tagBuilder = new TagBuilder("input"); 就是负者生成我们客户端的input控件,默认有 type,name,id属性,id属性的生成由以下code:

if (setId) {
tagBuilder.GenerateId(fullName);
}

整个方法的核心code 在  tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));

 public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata) {
            Dictionary<string, object> results = new Dictionary<string, object>();

            // The ordering of these 3 checks (and the early exits) is for performance reasons.
            if (!ViewContext.UnobtrusiveJavaScriptEnabled) {
                return results;
            }

            FormContext formContext = ViewContext.GetFormContextForClientValidation();
            if (formContext == null) {
                return results;
            }

            string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            if (formContext.RenderedField(fullName)) {
                return results;
            }

            formContext.RenderedField(fullName, true);

            IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);
            bool renderedRules = false;

            foreach (ModelClientValidationRule rule in clientRules) {
                renderedRules = true;
                string ruleName = "data-val-" + rule.ValidationType;

                ValidateUnobtrusiveValidationRule(rule, results, ruleName);

                results.Add(ruleName, HttpUtility.HtmlEncode(rule.ErrorMessage ?? String.Empty));
                ruleName += "-";

                foreach (var kvp in rule.ValidationParameters) {
                    results.Add(ruleName + kvp.Key, kvp.Value ?? String.Empty);
                }
            }

            if (renderedRules) {
                results.Add("data-val", "true");
            }

            return results;
        }

该方法首先检查 UnobtrusiveJavaScriptEnabled是否为true,FormContext是否存在, if (formContext.RenderedField(fullName)) 该控件是否已被Render。而这个方法的核心是 


IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);

获取ModelClientValidationRule集合,就开始追加属性了


foreach (ModelClientValidationRule rule in clientRules) {
renderedRules = true;
string ruleName = "data-val-" + rule.ValidationType; //获取客户端属性前缀

ValidateUnobtrusiveValidationRule(rule, results, ruleName);//验证客户端属性是否合法,不能有大写字母

results.Add(ruleName, HttpUtility.HtmlEncode(rule.ErrorMessage ?? String.Empty)); //追加errormessage的属性
ruleName += "-";

foreach (var kvp in rule.ValidationParameters) {
results.Add(ruleName + kvp.Key, kvp.Value ?? String.Empty); //追加参数属性, 如data-val-mulregular-minmatchno="3"

}
}

if (renderedRules) {
results.Add("data-val", "true"); //追加是否启用客户端验证
}

 public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
            if (viewContext == null) {
                throw new ArgumentNullException("viewContext");
            }
            if (viewDataContainer == null) {
                throw new ArgumentNullException("viewDataContainer");
            }
            if (routeCollection == null) {
                throw new ArgumentNullException("routeCollection");
            }

            ViewContext = viewContext;
            ViewDataContainer = viewDataContainer;
            RouteCollection = routeCollection;
            ClientValidationRuleFactory = (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, ViewData), ViewContext).SelectMany(v => v.GetClientValidationRules());
        }

mvc中很多code 都是采用Factory和Providers来实现的。这里的metadata一般都是有值的并且是System.Web.Mvc.DataAnnotationsModelMetadata类型。默认有DataAnnotationsModelValidatorProvider,DataErrorInfoModelValidatorProvider,ClientDataTypeModelValidatorProvider

namespace System.Web.Mvc {
    public static class ModelValidatorProviders {

        private static readonly ModelValidatorProviderCollection _providers = new ModelValidatorProviderCollection() {
            new DataAnnotationsModelValidatorProvider(),
            new DataErrorInfoModelValidatorProvider(),
            new ClientDataTypeModelValidatorProvider()
        };

        public static ModelValidatorProviderCollection Providers {
            get {
                return _providers;
            }
        }

    }
}

我们平时用的最多的应该是DataAnnotationsModelValidatorProvider,所以我们来看看

技术分享
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. All rights reserved.
 *
 * This software is subject to the Microsoft Public License (Ms-PL). 
 * A copy of the license can be found in the license.htm file included 
 * in this distribution.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * ***************************************************************************/

namespace System.Web.Mvc {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Web.Mvc.Resources;

    // A factory for validators based on ValidationAttribute
    public delegate ModelValidator DataAnnotationsModelValidationFactory(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute);

    // A factory for validators based on IValidatableObject
    public delegate ModelValidator DataAnnotationsValidatableObjectAdapterFactory(ModelMetadata metadata, ControllerContext context);

    /// <summary>
    /// An implementation of <see cref="ModelValidatorProvider"/> which providers validators
    /// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides
    /// a validator for types which implement <see cref="IValidatableObject"/>. To support
    /// client side validation, you can either register adapters through the static methods
    /// on this class, or by having your validation attributes implement
    /// <see cref="IClientValidatable"/>. The logic to support IClientValidatable
    /// is implemented in <see cref="DataAnnotationsModelValidator"/>.
    /// </summary>
    public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider {
        private static bool _addImplicitRequiredAttributeForValueTypes = true;
        private static ReaderWriterLockSlim _adaptersLock = new ReaderWriterLockSlim();

        // Factories for validation attributes

        internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
            (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute);

        internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>() {
            {
                typeof(RangeAttribute),
                (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)
            },
            {
                typeof(RegularExpressionAttribute),
                (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)
            },
            {
                typeof(RequiredAttribute),
                (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)
            },
            {
                typeof(StringLengthAttribute),
                (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)
            },
        };

        // Factories for IValidatableObject models

        internal static DataAnnotationsValidatableObjectAdapterFactory DefaultValidatableFactory =
            (metadata, context) => new ValidatableObjectAdapter(metadata, context);

        internal static Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory> ValidatableFactories = new Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory>();

        public static bool AddImplicitRequiredAttributeForValueTypes {
            get {
                return _addImplicitRequiredAttributeForValueTypes;
            }
            set {
                _addImplicitRequiredAttributeForValueTypes = value;
            }
        }

        protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {
            _adaptersLock.EnterReadLock();

            try {
                List<ModelValidator> results = new List<ModelValidator>();

                // Add an implied [Required] attribute for any non-nullable value type,
                // unless they‘ve configured us not to do that.
                if (AddImplicitRequiredAttributeForValueTypes &&
                        metadata.IsRequired &&
                        !attributes.Any(a => a is RequiredAttribute)) {
                    attributes = attributes.Concat(new[] { new RequiredAttribute() });
                }

                // Produce a validator for each validation attribute we find
                foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) {
                    DataAnnotationsModelValidationFactory factory;
                    if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) {
                        factory = DefaultAttributeFactory;
                    }
                    results.Add(factory(metadata, context, attribute));
                }

                // Produce a validator if the type supports IValidatableObject
                if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) {
                    DataAnnotationsValidatableObjectAdapterFactory factory;
                    if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory)) {
                        factory = DefaultValidatableFactory;
                    }
                    results.Add(factory(metadata, context));
                }

                return results;
            }
            finally {
                _adaptersLock.ExitReadLock();
            }
        }

        #region Validation attribute adapter registration

        public static void RegisterAdapter(Type attributeType, Type adapterType) {
            ValidateAttributeType(attributeType);
            ValidateAttributeAdapterType(adapterType);
            ConstructorInfo constructor = GetAttributeAdapterConstructor(attributeType, adapterType);

            _adaptersLock.EnterWriteLock();

            try {
                AttributeFactories[attributeType] = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });
            }
            finally {
                _adaptersLock.ExitWriteLock();
            }
        }

        public static void RegisterAdapterFactory(Type attributeType, DataAnnotationsModelValidationFactory factory) {
            ValidateAttributeType(attributeType);
            ValidateAttributeFactory(factory);

            _adaptersLock.EnterWriteLock();

            try {
                AttributeFactories[attributeType] = factory;
            }
            finally {
                _adaptersLock.ExitWriteLock();
            }
        }

        public static void RegisterDefaultAdapter(Type adapterType) {
            ValidateAttributeAdapterType(adapterType);
            ConstructorInfo constructor = GetAttributeAdapterConstructor(typeof(ValidationAttribute), adapterType);

            DefaultAttributeFactory = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });
        }

        public static void RegisterDefaultAdapterFactory(DataAnnotationsModelValidationFactory factory) {
            ValidateAttributeFactory(factory);

            DefaultAttributeFactory = factory;
        }

        // Helpers 

        private static ConstructorInfo GetAttributeAdapterConstructor(Type attributeType, Type adapterType) {
            ConstructorInfo constructor = adapterType.GetConstructor(new[] { typeof(ModelMetadata), typeof(ControllerContext), attributeType });
            if (constructor == null) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.DataAnnotationsModelValidatorProvider_ConstructorRequirements,
                        adapterType.FullName,
                        typeof(ModelMetadata).FullName,
                        typeof(ControllerContext).FullName,
                        attributeType.FullName
                    ),
                    "adapterType"
                );
            }

            return constructor;
        }

        private static void ValidateAttributeAdapterType(Type adapterType) {
            if (adapterType == null) {
                throw new ArgumentNullException("adapterType");
            }
            if (!typeof(ModelValidator).IsAssignableFrom(adapterType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.Common_TypeMustDriveFromType,
                        adapterType.FullName,
                        typeof(ModelValidator).FullName
                    ),
                    "adapterType"
                );
            }
        }

        private static void ValidateAttributeType(Type attributeType) {
            if (attributeType == null) {
                throw new ArgumentNullException("attributeType");
            }
            if (!typeof(ValidationAttribute).IsAssignableFrom(attributeType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.Common_TypeMustDriveFromType,
                        attributeType.FullName,
                        typeof(ValidationAttribute).FullName
                    ),
                    "attributeType");
            }
        }

        private static void ValidateAttributeFactory(DataAnnotationsModelValidationFactory factory) {
            if (factory == null) {
                throw new ArgumentNullException("factory");
            }
        }

        #endregion

        #region IValidatableObject adapter registration

        /// <summary>
        /// Registers an adapter type for the given <see cref="modelType"/>, which must
        /// implement <see cref="IValidatableObject"/>. The adapter type must derive from
        /// <see cref="ModelValidator"/> and it must contain a public constructor
        /// which takes two parameters of types <see cref="ModelMetadata"/> and
        /// <see cref="ControllerContext"/>.
        /// </summary>
        public static void RegisterValidatableObjectAdapter(Type modelType, Type adapterType) {
            ValidateValidatableModelType(modelType);
            ValidateValidatableAdapterType(adapterType);
            ConstructorInfo constructor = GetValidatableAdapterConstructor(adapterType);

            _adaptersLock.EnterWriteLock();

            try {
                ValidatableFactories[modelType] = (metadata, context) => (ModelValidator)constructor.Invoke(new object[] { metadata, context });
            }
            finally {
                _adaptersLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Registers an adapter factory for the given <see cref="modelType"/>, which must
        /// implement <see cref="IValidatableObject"/>.
        /// </summary>
        public static void RegisterValidatableObjectAdapterFactory(Type modelType, DataAnnotationsValidatableObjectAdapterFactory factory) {
            ValidateValidatableModelType(modelType);
            ValidateValidatableFactory(factory);

            _adaptersLock.EnterWriteLock();

            try {
                ValidatableFactories[modelType] = factory;
            }
            finally {
                _adaptersLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Registers the default adapter type for objects which implement
        /// <see cref="IValidatableObject"/>. The adapter type must derive from
        /// <see cref="ModelValidator"/> and it must contain a public constructor
        /// which takes two parameters of types <see cref="ModelMetadata"/> and
        /// <see cref="ControllerContext"/>.
        /// </summary>
        public static void RegisterDefaultValidatableObjectAdapter(Type adapterType) {
            ValidateValidatableAdapterType(adapterType);
            ConstructorInfo constructor = GetValidatableAdapterConstructor(adapterType);

            DefaultValidatableFactory = (metadata, context) => (ModelValidator)constructor.Invoke(new object[] { metadata, context });
        }

        /// <summary>
        /// Registers the default adapter factory for objects which implement
        /// <see cref="IValidatableObject"/>.
        /// </summary>
        public static void RegisterDefaultValidatableObjectAdapterFactory(DataAnnotationsValidatableObjectAdapterFactory factory) {
            ValidateValidatableFactory(factory);

            DefaultValidatableFactory = factory;
        }

        // Helpers 

        private static ConstructorInfo GetValidatableAdapterConstructor(Type adapterType) {
            ConstructorInfo constructor = adapterType.GetConstructor(new[] { typeof(ModelMetadata), typeof(ControllerContext) });
            if (constructor == null) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.DataAnnotationsModelValidatorProvider_ValidatableConstructorRequirements,
                        adapterType.FullName,
                        typeof(ModelMetadata).FullName,
                        typeof(ControllerContext).FullName
                    ),
                    "adapterType"
                );
            }

            return constructor;
        }

        private static void ValidateValidatableAdapterType(Type adapterType) {
            if (adapterType == null) {
                throw new ArgumentNullException("adapterType");
            }
            if (!typeof(ModelValidator).IsAssignableFrom(adapterType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.Common_TypeMustDriveFromType,
                        adapterType.FullName,
                        typeof(ModelValidator).FullName
                    ),
                    "adapterType");
            }
        }

        private static void ValidateValidatableModelType(Type modelType) {
            if (modelType == null) {
                throw new ArgumentNullException("modelType");
            }
            if (!typeof(IValidatableObject).IsAssignableFrom(modelType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.Common_TypeMustDriveFromType,
                        modelType.FullName,
                        typeof(IValidatableObject).FullName
                    ),
                    "modelType"
                );
            }
        }

        private static void ValidateValidatableFactory(DataAnnotationsValidatableObjectAdapterFactory factory) {
            if (factory == null) {
                throw new ArgumentNullException("factory");
            }
        }

        #endregion
    }
}
View Code

GetValidators方法的主要实现如下:

List<ModelValidator> results = new List<ModelValidator>();

// Add an implied [Required] attribute for any non-nullable value type,
// unless they‘ve configured us not to do that.
if (AddImplicitRequiredAttributeForValueTypes &&
metadata.IsRequired &&
!attributes.Any(a => a is RequiredAttribute)) {
attributes = attributes.Concat(new[] { new RequiredAttribute() });
}

// Produce a validator for each validation attribute we find
foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) {
DataAnnotationsModelValidationFactory factory;
if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) {
factory = DefaultAttributeFactory;
}
results.Add(factory(metadata, context, attribute));
}

// Produce a validator if the type supports IValidatableObject
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) {
DataAnnotationsValidatableObjectAdapterFactory factory;
if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory)) {
factory = DefaultValidatableFactory;
}
results.Add(factory(metadata, context));
}

return results;

我们可以从AttributeFactories和ValidatableFactories里面找到我们需要的factory,DataAnnotationsModelValidationFactory 默认有:

RangeAttribute->RangeAttributeAdapter

RegularExpressionAttribute->RegularExpressionAttributeAdapter

RequiredAttribute->RequiredAttributeAdapter

StringLengthAttribute->StringLengthAttributeAdapter

而这4个adapter都继承与 public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator where TAttribute : ValidationAttribute ,并且都从写了基类的GetClientValidationRules方法。以RangeAttributeAdapter 的为例 ,其实现如下:

技术分享
  public class RangeAttributeAdapter : DataAnnotationsModelValidator<RangeAttribute> {
        public RangeAttributeAdapter(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute)
            : base(metadata, context, attribute) {
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
            string errorMessage = ErrorMessage; // Per Dev10 Bug #923283, need to make sure ErrorMessage is called before Minimum/Maximum
            return new[] { new ModelClientValidationRangeRule(errorMessage, Attribute.Minimum, Attribute.Maximum) };
        }
    }
 public class ModelClientValidationRangeRule : ModelClientValidationRule {
        public ModelClientValidationRangeRule(string errorMessage, object minValue, object maxValue) {
            ErrorMessage = errorMessage;
            ValidationType = "range";
            ValidationParameters["min"] = minValue;
            ValidationParameters["max"] = maxValue;
        }
    }
View Code

这里的AttributeFactories可以通过

public static void RegisterAdapter(Type attributeType, Type adapterType)

  public static void RegisterAdapterFactory(Type attributeType, DataAnnotationsModelValidationFactory factory)

这2个方法添加新的成员。而ValidatableFactories默认是没有成员的,可以通过以下方法添加成员

 public static void RegisterValidatableObjectAdapter(Type modelType, Type adapterType)

  public static void RegisterValidatableObjectAdapterFactory(Type modelType, DataAnnotationsValidatableObjectAdapterFactory factory)

注意以下我们一般自定义的验证类都是继承ValidationAttribute的,所以这里用的就是DefaultAttributeFactory,也就是DataAnnotationsModelValidator,那么就是要调用DataAnnotationsModelValidator的GetClientValidationRules方法。

public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
IEnumerable<ModelClientValidationRule> results = base.GetClientValidationRules();

IClientValidatable clientValidatable = Attribute as IClientValidatable;
if (clientValidatable != null) {
results = results.Concat(clientValidatable.GetClientValidationRules(Metadata, ControllerContext));
}

return results;
}

这个方法首先调用基类的GetClientValidationRules方法,返回没有元素的一个集合,然后调用我们自定义类的GetClientValidationRules方法,这里也就解释了为什么 我们需要实现IClientValidatable 这个接口了。

asp.net mvc源码分析-ModelValidatorProviders 客户端的验证

标签:

原文地址:http://www.cnblogs.com/majiang/p/5346935.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!