您的位置:首页 > 编程语言 > ASP

asp.net mvc源码分析-ModelValidatorProviders

2012-11-14 16:39 751 查看
在上篇文章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);

});

具体是怎么验证的我这里就忽略了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: