zoukankan      html  css  js  c++  java
  • asp.net mvc源码分析ModelValidatorProviders

    在上篇文章asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证最后提到了ModelValidatorProviders ,这里我们以DataAnnotationsModelValidatorProvider来说说整过的验证过程。因为 DataAnnotationsModelValidatorProvider这个是我们平时用的最多的情况。其GetValidators的具体实现如 下:

      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();
                }
            }
    

     首先从这里的特性取出是ValidationAttribute特性的attribute,然后根据attribute的type取出相应的DataAnnotationsModelValidationFactory,其中以下4个特性和其DataAnnotationsModelValidationFactory返回的ModelValidator实例一一对应:
    RangeAttribute->RangeAttributeAdapter
    RegularExpressionAttribute->RegularExpressionAttributeAdapter
    RequiredAttribute->RequiredAttributeAdapter
    StringLengthAttribute->StringLengthAttributeAdapter

    这里的4个AttributeAdapter都继承于 DataAnnotationsModelValidator<TAttribute>   -> DataAnnotationsModelValidator ->ModelValidator.

    如果没有取消相应的DataAnnotationsModelValidationFactory就调用默认的 DefaultValidatableFactory,它返回一个ValidatableObjectAdapter的一个实例;然后依次调用它们的 Validate方法。以上面4个attribute为例,它们的Validate方法默认的实现在 DataAnnotationsModelValidator中的Validate方法:


            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
                    };
                }
            }

    调用每个Attribute的GetValidationResult方法。验证失败就会返 回一个ModelValidationResult实例。到这里我们知道DataAnnotationsModelValidator的Validate 方法是怎么调用delete,但是这里类里面还有一个GetClientValidationRules方法,它好像是一个客户端的验证啊。调用 attribute的GetClientValidationRules方法。该方法是在哪里调用的了。我们有时候在view里面有这样的方 法 @Html.ValidationMessageFor(model => model.UserName)。

    ValidationMessage方法中有这么一段

      if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled) {
                        builder.MergeAttribute("data-valmsg-for", modelName);
                        builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
                    }
                    else {
                        FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
                        // rules will already have been written to the metadata object
                        fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified

                        // client validation always requires an ID
                        builder.GenerateId(modelName + "_validationMessage");
                        fieldMetadata.ValidationMessageId = builder.Attributes["id"];
                    }

    最终会调用一个ApplyFieldValidationMetadata方法,不过默认的UnobtrusiveJavaScriptEnabled为true。

       private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName) {
                FormContext formContext = htmlHelper.ViewContext.FormContext;
                FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);

                // write rules to context object
                IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
                foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules())) {
                    fieldMetadata.ValidationRules.Add(rule);
                }


                return fieldMetadata;
            }
    这里会依次调用我们ModelValidator的GetClientValidationRules方法。
    例如我们的代码调用如下:

    如果UnobtrusiveJavaScriptEnabled为true下的html如下:

    UnobtrusiveJavaScriptEnabled为false的html如下:

    我们以UnobtrusiveJavaScriptEnabled为true来说说客服端验证。在生成的html中data-val="true"表示要启用客户端验证,data-val-表示我们要验证的属性。负责验证的js是jquery.validate.unobtrusive.js,里面有这么一句:

     $(selector).find(":input[data-val=true]").each(function () {
                    $jQval.unobtrusive.parseElement(this, true);
                });

    具体是怎么验证的我这里就忽略了.

     

  • 相关阅读:
    20170411linux常用命令
    20170411oracle常用命令
    20170411-oracle 查询指定节点下的所有子节点包括直到叶子节点
    20170329oracle安装教程
    20170329plsql连接oracle
    20170329001怎么让plsql窗口列表保持
    Eclispse 换主题、皮肤、配色,换黑色主题护眼
    zbb20170303使用ssh一直找不到session,报错not found session in current thread
    zbb20170303_ant_build.xml详解
    hdu Farm Irrigation
  • 原文地址:https://www.cnblogs.com/majiang/p/2770174.html
Copyright © 2011-2022 走看看