ModelState.IsValid为true,但是POST数据中缺少[Required]属性

Ale*_*lex 2 asp.net asp.net-mvc-3

我的视图模型具有一个[必需]不可为空的int属性,该属性由DropDownListFor选择。如果要选择的列表为空,则ModelState.IsValid为true。

我的模型具有Required int属性:

public class MyModel
{
    [Required]
    public int PickedValue  { get; set;}

    IEnumerable<SelectListItem> Items { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在“创建”视图中,渲染一个<select>元素:

@Html.DropDownListFor(model => model.PickedValue, model.Items)
Run Code Online (Sandbox Code Playgroud)

在我的控制器中,如果model.Items列表为空(没有可供选择的元素),ModelState.IsValid则为true:

[HttpPost]
public ActionResult Create( MyModel model )
{
    if( ModelState.IsValid )
    {
        // true, and ModelState.Keys doesn't contain PickedValue because it was never POSTed.
    }
    //...
 }
Run Code Online (Sandbox Code Playgroud)

现在,如果出现以下情况,问题就消失了:

  1. PickedValueNullableint?),或
  2. 如果我的<select>- @Html.DropDownListFor(model => model.PickedValue, model.Items, "")中有一个空项目,或者
  3. 如果启用了客户端验证(因为从未触发该操作)。

是否有一种方法可以强制ModelState.IsValid执行false某些MyModel属性,[Required]但POST数据中缺少这些属性?这不是默认行为吗?

dan*_*wig 5

就您而言,我认为将PickedValue设置为可为null的int可能会更好。如果您的DropDownList没有项目的可能性,则PickedValue的可能值为null。

回复评论

这是有道理的,但不能回答我的原始问题...如果MyModel的某些属性为[Required],但POST数据中缺少这些属性,是否有办法强制ModelState.IsValid为false?这不是默认行为吗?

对于int,double,bool和其他不能具有null值的原语,当DefaultModelBinder在HTTP请求中未为其接收任何数据时,它仍必须构造一个模型实例。因此,将构建模型并将属性初始化为它们的默认值。如果是int,则意味着它将初始化为零。由于HTTP请求中未收到任何内容,因此这意味着int永远不会被设置,因此它将始终为零。您是否尝试过给int一个[Range]验证器而不是一个[Required]验证器?这应该验证它不为零:

[Range(1, int.MaxValue)]
public int PickedValue  { get; set; }
Run Code Online (Sandbox Code Playgroud)

我想避免更改模型,因为它已经是我代码中的常见模式了……再加上使模型中的属性可为空以强制其不为null似乎违反直觉。

仅仅因为它是代码中的常见模式并不意味着它是正确的。我要重申我原来的答复:如果有可能你的下拉列表中没有任何项目,那么你的模型应该允许null其选择的值。

这就是为什么最好有单独的实体和视图模型层的原因之一。在您的域实体中,可能需要一个关系。但是,当首次向用户显示一个用于选择该关系的表单时,您要么必须给他们提供默认值,要么将选择的空白项留空。在这种情况下,实体中的外键可能是一个int,但viewmodel中的表示形式应该是一个Nullable<int>。仅出于这个原因,我认为使某些内容为可空值仅仅是为了确保它不是空值是违反直觉的。您可以在视图模型中将其设置为可空的,以便为用户提供带有空白值的表单,并要求他们填写该表单。