Asp.net MVC源码分析--Model Validation(Server端)实现(1)

一.MVC Validation 用法:

在Asp.net MVC 框架中如果需要对Model 对象加入验证,我们可以在Model的属性上标记所有继承于ValidationAttribute的Attribute特性. 

例如下面的代码中,StringLength/Range/Compare 都是继承于ValidationAttribute类.

 public class LogOnModel
{
[Required]
[StringLength(10)]
public string UserName { get; set; }

[Required]
[Range(5,10)]
public string Password { get; set; }

[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }

[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

在Action 中我们可以调用 ValidateModel 方法对Model对象进行验证,如果验证没有通过则会抛出InvalidOperationException异常,同时ModelState.IsValid状态为false

   [HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
this.ValidateModel(model);
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

这里需要注意的是如果我们不调用ValidateModel 方法Action上的Model对象默认也是能够验证的,这是因为在绑定对象阶段DefaultModelBinder.BindComplexElementalModel方法中MVC触发了验证的方法。

DefaultModelBinder.cs

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

二.ValidateModel方法源码分析

下面我们看一下MVC 是如何实现ValidateModel方法验证的.

 1  protected internal void ValidateModel(object model) {
2 ValidateModel(model, null /* prefix */);
3 }
4
5 protected internal void ValidateModel(object model, string prefix) {
6 if (!TryValidateModel(model, prefix)) {
7 throw new InvalidOperationException(
8 String.Format(
9 CultureInfo.CurrentCulture,
10 MvcResources.Controller_Validate_ValidationFailed,
11 model.GetType().FullName
12 )
13 );
14 }
15 }
16
17 protected internal bool TryValidateModel(object model) {
18 return TryValidateModel(model, null /* prefix */);
19 }
20
21 protected internal bool TryValidateModel(object model, string prefix) {
22 if (model == null) {
23 throw new ArgumentNullException("model");
24 }
25
26 ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
27
28 foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, ControllerContext).Validate(null)) {
29 ModelState.AddModelError(DefaultModelBinder.CreateSubPropertyName(prefix, validationResult.MemberName), validationResult.Message);
30 }
31
32 return ModelState.IsValid;
33 }

我们可以看到第28行MVC是调用了ModelValidator.GetModelValidator 来获取Model 的Validator 对象的.我们看一下这个方法的实现,它返回的是CompositeModelValidator对象.

   public static ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context) {
return new CompositeModelValidator(metadata, context);
}
CompositeModelValidator.cs 
 1  private class CompositeModelValidator : ModelValidator {
2 public CompositeModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
3 : base(metadata, controllerContext) {
4 }
5
6 public override IEnumerable<ModelValidationResult> Validate(object container) {
7 bool propertiesValid = true;
8
9 foreach (ModelMetadata propertyMetadata in Metadata.Properties) {
10 foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(ControllerContext)) {
11 //Robbin:一但Validate返回了ModelValidationResult就证明验证没有通过.
12 foreach (ModelValidationResult propertyResult in propertyValidator.Validate(Metadata.Model)) {
13 propertiesValid = false;
14 yield return new ModelValidationResult {
15 MemberName = DefaultModelBinder.CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName),
16 Message = propertyResult.Message
17 };
18 }
19 }
20 }
21
22 if (propertiesValid) {
23 foreach (ModelValidator typeValidator in Metadata.GetValidators(ControllerContext)) {
24 foreach (ModelValidationResult typeResult in typeValidator.Validate(container)) {
25 yield return typeResult;
26 }
27 }
28 }
29 }
30 }
我们再看一下上面第10行,propertyMetadata.GetValidators这个方法的实现,他返回了所有的Validator的实现。并且在12行,调用这些实现的Validate方法进行验证操作.
public virtual IEnumerable<ModelValidator> GetValidators(ControllerContext context) {
return ModelValidatorProviders.Providers.GetValidators(this, context);
}

它调用了ModelValidatorProviders.Providers对象的GetValidators 方法. 这个对象的类型是ModelValidatorProviderCollection接下来我们看一下ModelValidatorProviders.Providers的实现

  public static class ModelValidatorProviders {

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

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

}

这个对象初始化时默认包括了三个ValidatorProvider,在这里我们需要着重注意的是DataAnnotationsModelValidatorProvider,它提供了所有系统默认的Validation规则(除了RemoteAttribute).

下一篇我们来详细的分析一下DataAnnotationsModelValidatorProvider的实现以及使用的设计模式.

 

转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289228.html 

本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

posted @ 2011-12-16 17:52  十一月的雨  阅读(2614)  评论(0编辑  收藏  举报