一.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
|
请发表评论