• 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

  • 相关阅读:
    2. Get the codes from GIT
    1. Install Git and GitExtension
    [Git]Git的常用命令
    测试用例Excel模板For Quality Center
    测试用例excel模板
    如何在win 2008 server和win 7上add web site
    Jmeter运行过程中如何让Fiddler同时可以抓获到服务器的应答报文
    Windows系统文件mshtml.dll
    Jmeter中的XPath Assertion
    AIM Tech Round (Div. 2) B
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2289228.html
Copyright © 2020-2023  润新知