在上篇文章最后提到了ModelValidatorProviders ,这里我们以DataAnnotationsModelValidatorProvider来说说整过的验证过程。因为 DataAnnotationsModelValidatorProvider这个是我们平时用的最多的情况。其GetValidators的具体实现如 下:
protected override IEnumerableGetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable attributes) { _adaptersLock.EnterReadLock(); try { List results = new List (); // 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 ()) { 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); });具体是怎么验证的我这里就忽略了.