【问题标题】:Validating a view model after custom model binding在自定义模型绑定后验证视图模型
【发布时间】:2012-11-20 23:36:53
【问题描述】:

我有一个实现 IValidatableObject 的视图模型,其中包含一个字符串和另一个视图模型的集合,如下所示:

public sealed class MainViewModel
{
    public string Name { get; set; }
    public ICollection<OtherViewModel> Others { get; set; }
}

我的验证使用IValidatableObject 提供的合约根据不同的规则检查Others 中的每个对象:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    foreach (var other in this.Others)
    {
        // validate or yield return new ValidationResult
    }
}

由于真实MainViewModel 的复杂结构,我不得不创建一个自定义模型绑定器,它重新构建模型并将POST 数据分配给相关组件。我遇到的问题是没有得到验证,导致上下文级别的验证错误,因为它违反了某些数据库约束,我不确定我做错了什么 - 我假设 ModelState.IsValid 会调用 @我的视图模型上的 987654328@ 方法,但它似乎并没有这样下去。

我的模型活页夹看起来像这样:

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    int modelId = (int)controllerContext.RouteData.Values["id"];

    // query the database and re-build the components of the view model

    // iterate the POST data and assign to the model where necessary

    // should I be calling something here to validate the model before it's passed to the controller?

    return model;
}

任何帮助表示赞赏!

Validator.TryValidateObject

好吧,看来我离得更近了一点。我现在可以通过将以下内容添加到我的自定义模型绑定器来运行我的 IValidatableObject 方法:

var validationResults = new HashSet<ValidationResult>();
var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);

似乎Validator.TryValidateObject 调用了验证方法并将最后一个参数设置为true 导致它验证所有属性。但是,我现在坚持将validationResults 传递给控制器​​,以便以有意义的方式使用它们。

【问题讨论】:

    标签: asp.net-mvc-3 validation custom-model-binder


    【解决方案1】:

    Paul 的出色答案可以重构为通用的验证并转换为 ModelState 方法,如下所示(例如在帮助程序或 CustomModelBinder 基础中)。此外,还保留了与已验证属性的绑定。

    public static void DoValidation(ModelBindingContext bindingContext, 
                                    IValidatableObject model)
    {
        var validationResults = new HashSet<ValidationResult>();
        var isValid = Validator.TryValidateObject(model, 
            new ValidationContext(model, null, null), validationResults, true);
        if (!isValid)
        {
            var resultsGroupedByMembers = validationResults
                .SelectMany(_ => _.MemberNames.Select(
                     x => new {MemberName = x ?? "", 
                               Error = _.ErrorMessage}))
                .GroupBy(_ => _.MemberName);
    
            foreach (var member in resultsGroupedByMembers)
            {
                bindingContext.ModelState.AddModelError(
                    member.Key,
                    string.Join(". ", member.Select(_ => _.Error)));
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我应该意识到我可以通过自定义绑定器使用ModelState.AddModelError,现在通过在将模型返回到控制器之前将以下内容添加到我的自定义模型绑定器中,我已经设法使其正常工作:

      var validationResults = new HashSet<ValidationResult>();
      var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);
      if (!isValid)
      {
          foreach (var result in validationResults)
          {
              bindingContext.ModelState.AddModelError("", result.ErrorMessage);
          }
      }
      
      return model;
      

      现在这会将所有错误的列表返回到我的页面,并且对我的控制器操作的 ModelState.IsValid 检查现在返回 false。

      【讨论】:

      • 我需要将此代码放在哪里:控制器和动作以及模型活页夹和哪里?实际上我遇到了这个错误,请帮忙。
      • @Rajpurohit 你应该把上面的代码放在你的自定义模型绑定器类中。
      • 在formeach中,还可以获取MemberNames。 string memberName = (result.MemberNames != null && result.MemberNames.Count() > 0) ? result.MemberNames.First() : string.Empty; bindingContext.ModelState.AddModelError(memberName, result.ErrorMessage);
      猜你喜欢
      • 2012-01-29
      • 1970-01-01
      • 2019-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-18
      相关资源
      最近更新 更多