【问题标题】:Why does MVC use the Modelstate over a supplied Model on a GET为什么 MVC 在 GET 上使用 Modelstate 而不是提供的 Model
【发布时间】:2018-06-20 15:04:17
【问题描述】:

当 MVC 运行 ActionMethod 时,它将填充 ModelState 字典并使用 ModelBinder 来构建 ActionMethod 参数(如果有)。它对GETPOST 都执行此操作。这是有道理的。

ActionMethod 成功运行后,使用提供的剃须刀呈现视图,在我的例子中,它使用尽可能多的HtmlHelper 调用。到目前为止,您可能会想,'是的,我知道 MVC 是如何工作的'。等一下,我马上就到了。

当我们使用例如@Html.TextFor(m => m.Name)MVC 使用可以为here 的代码来渲染标签。

有趣的部分在我们找到的文件末尾:

switch (inputType)
{
    case InputType.CheckBox:
        // ... removed for brevity
    case InputType.Radio:
    // ... removed for brevity
    case InputType.Password:
    // ... removed for brevity
    default:
        string attemptedValue = 
               (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
        tagBuilder.MergeAttribute("value", attemptedValue ?? 
               ((useViewData) 
                  ? htmlHelper.EvalString(fullName, format) 
                  : valueParameter), isExplicitValue);
        break;
} 

这意味着Modelstate 用于获取提供的Model 的值。这对于POST 是有意义的,因为当出现验证错误时,您希望 MVC 使用用户已经提供的值呈现视图。该文档还说明了这种行为,StackOverflow 上有几篇文章证实了这一点。但它仅适用于POST,例如在this thread 上可以看到

但是,当为GET 呈现视图时,也会使用此代码!这对我来说毫无意义。

考虑以下操作方法:

public ActionResult GetCopy(Guid id)
{
    var originalModel = this.repository.GetById(id);
    var copiedModel = new SomeModel
    {
        Id = Guid.NewGuid(),
        Name = originalModel.Name,
    };

    return this.View(copiedModel);
}

使用这个简单的剃​​刀标记:

    @Html.TextBoxFor(m => m.Id)
    @Html.TextBoxFor(m => m.Name)

我希望视图显示新生成的GUID,而不是原始模型的Id。然而,视图显示 Id 传递给 GET 请求上的操作方法,因为 ModelState 字典包含名称为 Id 的键。

我的问题不是如何解决这个问题,因为这很容易:

  • ActionMethod 参数重命名为不同的名称
  • 使用Modelstate.Clear() 在返回ActionResult 之前清除ModelState
  • 替换 ModelState 字典中的值,并且根本不向视图提供 Model

我的问题是: 为什么GETPOST 的过程相同。在GET 上使用填充的ModelState 而不是提供的Model 是否有任何有效的用例。

【问题讨论】:

  • DefaultModelBinder 不区分 GET 或 POST。但是为什么你认为行为应该不同呢?如果我向一个方法发出请求并传递一个值,那么我希望我传递的值被使用,而不是一些完全不同的值
  • @StephenMuecke 一般来说,我希望如果我将一个对象传递给一个方法,它将被使用,而不是来自环境上下文的数据。所以this.View(model)在我看来会使用model的数据。
  • 而且我希望如果我有(比如说)一个制作 GET 的表单(比如说传递一些用于过滤记录的“搜索”值),那么这些值将再次返回。 (我有点不确定如何处理这个问题 - 投票,因为它写得很好,研究得很好,和/或投票关闭它,因为它征求意见:)
  • 问题是是否有任何有效的用例
  • 我在之前的评论中给了你我的看法:)

标签: c# asp.net-mvc razor modelstate


【解决方案1】:

DefaultModelBinder 不区分 GET 和 POST 请求。虽然只有 MVC 团队可以确认他们做出该决定的原因,但有一些有效的用例为什么应该在 GET 中使用 ModelState(与在 POST 中使用的方式相同),因为您也可以将表单提交给 GET 方法.

以航空公司预订应用为例,您有一个模型,其中包含用于选择出发和到达地点、日期和乘客数量(int 属性)的属性,以及用于过滤结果的集合属性。 GET 方法签名可能看起来像

public ActionResult Search(SearchViewModel model)

该视图包含一个返回该方法的 GET 并包含一个用于显示乘客数量的文本框。

禁用 javascript 的(不太聪明的)用户在文本框中输入“TWO”并提交表单。您的方法在不填充可用航班集合的情况下返回视图,因为 ModelState 无效(值“TWO”对于 int 无效),现在用户看到一条错误消息,指出 字段乘客必须是一个数字

如果使用模型值而不是 ModelState 值,则关联的文本框将显示 0 而不是 "Two",从而使错误消息令人困惑(但 0 是一个数字!- 以及什么我输入的内容发生了什么变化?)

【讨论】:

  • 感谢您的详细解答!我想有足够的有效用例可以在大多数情况下使这确实成为所需的行为。虽然我仍然希望重载带有一些 bool 标志,以便清楚地确定要使用的值。
猜你喜欢
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多