【问题标题】:asp.net mvc validation problemasp.net mvc验证问题
【发布时间】:2011-04-12 20:20:21
【问题描述】:

这对我来说没有任何意义。

看看这个 ViewModel:

public class SelectService
{
    [Required]
    public int? SelectedServiceId { get; set; }
}

看看这个动作:

[HttpPost]
public virtual ActionResult SelectService()
{
    TryUpdateModel(WizardContainer.SelectService, "SelectService");
    TryValidateModel(WizardContainer.SelectService, "SelectService"); // if I remove this, ModelState.IsValid will always be true


    if (ModelState.IsValid)
    {
        return RedirectToAction("OtherAction");
    }
    else
    {
        return View(WizardContainer);
    }
}

现在阅读 S. Sanderson 的 Apress ASP.NET MVC 2 Framework 节选:

每当您使用模型绑定来填充模型对象时 - 通过将其作为操作方法接收 参数,或者通过手动调用 UpdateModel() 或 TryUpdateModel() - 然后 DefaultModelBinder 将 自动运行与它已更新的所有模型对象关联的验证器(即,它在 已在至少一个属性上设置了值)。 如果您以任何其他方式更新模型对象,除非您明确告知,否则不会运行其验证器 运行它们的框架。

那么,为什么当我调用 TryUpdateModel() 时从未发生验证?要进行验证,我必须使用 TryValidateModel() 显式验证。

更新

这是在同一个控制器中按预期工作的非常相似的操作:

[HttpPost]
public virtual ActionResult Index(string nextButton)
{
    TryUpdateModel(WizardContainer.Index);

    if (nextButton != null && ModelState.IsValid)
    {
        return RedirectToAction("OtherAction");
    }
    else
    {
        return View(WizardContainer.Index);
    }
}

这是 ViewModel:

public class Index
{
    [Required]
    public DateTime? SelectedServiceTime { get; set; }
}

更新 2

我改变了一些东西,现在它的行为符合预期,但我仍然不明白为什么。

查看编辑后的动作:

[HttpPost]
public virtual ActionResult SelectService()
{
    TryUpdateModel(WizardContainer.SelectService);
    //TryValidateModel(WizardContainer.SelectService, "SelectService"); // not needed anymore

    if (ModelState.IsValid)
    {
        return RedirectToAction("OtherMethod");
    }
    else
    {
        return View(WizardContainer.SelectService);
    }
}

现在的区别只是我传递给 TryUpdateModel() 的对象是我传递给视图的对象,而不是我传递给视图的对象的属性。什么交易哟?

【问题讨论】:

  • 如果使用 int 而不是可为空的 int 会发生什么?您将可空参数标记为必需的,这对于 Web 请求没有意义,因为可空意味着根本没有提供任何值(请求中不包含参数)。允许空值但将属性标记为Required 会产生冲突的验证逻辑。
  • @Nathan - 您不能要求值类型。它总是有价值的。您应该使用可为空的 int。
  • @Bigglesby 好的,我没有考虑到这一点。好点子。
  • 您是否设置了一个不会自动执行验证的自定义模型绑定器?
  • @Adrian - 没有机会。我没有注册任何其他模型绑定器,也没有装饰任何自定义模型以使用任何其他绑定器。

标签: c# .net asp.net-mvc asp.net-mvc-2


【解决方案1】:

我在下面包含了TryUpdateModelTryValidateModel 方法的反编译。我看到的主要区别是TryUpdateModel 的返回是ModelState.IsValid,但我没有看到TryUpdateModel 中验证器的显式执行。

TryValidateModel 实际上获取了验证器并执行每个验证器。您的工作案例和非工作案例之间必须存在一些细微差别。我不能说根本原因是什么,但在您的工作案例中,当您调用TryUpdateModel 时,ModelState.Errors 集合中已经添加了ModelState 错误。 (如果ModelState.Errors 集合中有一个或多个错误,ModelState.IsValid 返回 false。)

TryUpdateModel:

protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel: class
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }
    if (valueProvider == null)
    {
        throw new ArgumentNullException("valueProvider");
    }
    Predicate<string> predicate = delegate (string propertyName) {
        return BindAttribute.IsPropertyAllowed(propertyName, base.includeProperties, base.excludeProperties);
    };
    IModelBinder binder = this.Binders.GetBinder(typeof(TModel));
    ModelBindingContext context2 = new ModelBindingContext();
    context2.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(delegate {
        return base.model;
    }, typeof(TModel));
    context2.ModelName = prefix;
    context2.ModelState = this.ModelState;
    context2.PropertyFilter = predicate;
    context2.ValueProvider = valueProvider;
    ModelBindingContext bindingContext = context2;
    binder.BindModel(base.ControllerContext, bindingContext);
    return this.ModelState.IsValid;
}

TryValidateModel:

protected internal bool TryValidateModel(object model, string prefix)
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }
    foreach (ModelValidationResult result in ModelValidator.GetModelValidator(ModelMetadataProviders.Current.GetMetadataForType(delegate {
        return model;
    }, model.GetType()), base.ControllerContext).Validate(null))
    {
        this.ModelState.AddModelError(DefaultModelBinder.CreateSubPropertyName(prefix, result.MemberName), result.Message);
    }
    return this.ModelState.IsValid;
}

【讨论】:

  • 我想你是在问一个问题。在工作方法中,ModelState.IsValid = true 在尝试更新模型之前。如果请求中未提供所需的值,则为 false。
  • 验证不会发生在 TryUpdateModel() 本身的主体中,而是发生在对 DefaultModelBinder.BindModel() 的调用中,该调用调用 BindProperty(),其中错误实际上是在 ModelState 中注册的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-24
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
  • 2011-07-31
相关资源
最近更新 更多