将隐藏字段添加到模型时,MVC 范围属性失败

MVC Range Attribute fails when a hidden field is added to the model

我有一个包含 属性 的父模型,子模型使用它来预填充视图中的字段。 我想将 属性 移动到它所属的子模型中,但是当我这样做时另一个 属性 的范围属性失败。 当我有一个仅用于隐藏在 EditorTemplate 上的 属性 时,为什么范围属性验证失败?

模特长得像

public class ParentModel
{
    public SubModel subModel { get; set; }
}
public class SubModel
{
    public uint? DefaultValue { get; set; }

    [Required]
    [Range(1,100)]
    public uint RangedId { get; set;}

    public bool EnableRange { get; set; }
}

视图(EditorTemplate)看起来像

@model SubModel
@Html.HiddenFor(model => model.DefaultValue)
@Html.TextBoxFor(model => model.RangeId)
<script>
     $('#EnableRange').change(function() {
         if($('#EnableRange').val()){
             // remove the disabled attribute from the RangeId Field
         } else {
             // add the disabled attribute from the RangeId Field
         }
     }
</script>

控制器看起来像

public ActionResult Create(TViewModel model)
{
    try
    {
        if (ModelState.IsValid)
        {
            //Do Something Meaningful
        }
        //Redisplay the view
    }
}

使用子模型中的默认值 属性,即使在表单上禁用了 RangeId,RangeId 的范围属性也会触发。这导致 ModelState.IsValid 为假。 当我将 DefaultValue 属性 向上移动到 ParentModel 时,RangeId 的 Range 属性不再触发(因为该字段已禁用)。这导致 ModelState.IsValid 为真,因为从不评估 RangeId 以进行验证。

无论你认为发生了什么,都没有发生。服务器端 Model.IsValid 不关心任何事情,也不会直接受到禁用客户端控件的影响(尽管它可能会受到间接影响,我们将在下面看到)。如果嵌套表单字段 posted 并且嵌套对象具有必需的属性,则始终会进行验证。

更可能的是,这里的真正问题是当您在子模型中有 DefaultValue 时,然后当您将模型提交给父模型时,模型绑定器会创建 SubModel 的实例,因为它包含 DefaultValue 的值。当您将它移动到父级并禁用 RangeId 时,post 没有值,因此不会创建 SubModel,因此不会发生验证。

也就是说,我的猜测是,当您将 DefaultValue 移至父级时,SubModel 在 post 后面为空,因此因为没有要验证的实例,所以没有任何验证失败,特别是因为您是不保留 EnableRange 值。

所以你确实有几个问题。首先,禁用客户端上的控件不会禁用服务器上的验证。其次,如果没有嵌套的表单字段 post 发送到服务器,则不会创建嵌套对象并且不会发生验证(因此在这方面,如果您非常喜欢,可以禁用验证作为副作用小心)。第三,如果您 post 一些嵌套表单字段而不是其他表单字段,那么将创建嵌套对象进行验证,并且将在未 post 编辑的字段上进行验证,因为它们已被禁用。