为什么仅在属性级验证器之后评估模块级验证器?

时间:2010-05-30 04:19:43

标签: asp.net-mvc asp.net-mvc-2-validation validationattribute

我正在使用模块级验证器:我的视图模型上的'PropertiesMustMatch',如下所示:

[PropertiesMustMatch("Password", "PasswordConfirm")]
public class HomeIndex
{
    [Required]
    public string Name { get; set; }

    public string Password { get; set; }

    public string PasswordConfirm { get; set; }
}

我注意到如果我提交没有填写Name的表单,ValidationSummary()帮助程序只返回以下错误:

  
      
  • 名称字段是必需的。
  •   

但是,如果我填写名称,那么 ValidationSummary()将返回一个PropertiesMustMatch错误:

  
      
  • '密码'和'密码确认'不匹配。
  •   

所以看起来首先评估属性级验证器,然后是模型级验证器。

如果他们一次全部验证我会更喜欢,ValidationSummary会返回:

  
      
  • 名称字段是必需的。
  •   
  • '密码'和'密码确认'不匹配。
  •   

我有什么想法可以解决这个问题吗?

我正在研究MVC 2源代码,试图确定为什么会发生这种情况。

1 个答案:

答案 0 :(得分:0)

我发现了导致这种情况的原因,但我的“解决方案”可能会打破验证器的正常处理。请谨慎使用。

我在DefaultModelBinder的OnModelUpdated函数中找到了一个条件return语句:

protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    IDataErrorInfo errorProvider = bindingContext.Model as IDataErrorInfo;
    if (errorProvider != null)
    {
        string errorText = errorProvider.Error;
        if (!String.IsNullOrEmpty(errorText))
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorText);
        }
    }

    // BEGIN CONDITION
    if (!IsModelValid(bindingContext))
    {
        return;
    }
    // END CONDITION

    foreach (ModelValidator validator in bindingContext.ModelMetadata.GetValidators(controllerContext))
    {
        foreach (ModelValidationResult validationResult in validator.Validate(null))
        {
            bindingContext.ModelState.AddModelError(CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName), validationResult.Message);
        }
    }
}

如果我理解这段代码(我可能不会这样做),那么MVC团队似乎会在此时跳过模型验证器。

我已经创建了自己的自定义ModelBinder,我在其中重新运行了条件可以避免的代码:

public class CustomModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        base.OnModelUpdated(controllerContext, bindingContext);

        foreach (ModelValidator validator in bindingContext.ModelMetadata.GetValidators(controllerContext))
        {
            foreach (ModelValidationResult validationResult in validator.Validate(null))
            {
                bindingContext.ModelState.AddModelError(CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName), validationResult.Message);
            }
        }
    }
}

这似乎解决了这个问题。