为什么 MVC 在 GET 上使用模型状态而不是提供的模型
Why does MVC use the Modelstate over a supplied Model on a GET
当 MVC 运行 ActionMethod
时,它将填充 ModelState
字典并使用 ModelBinder
构建 ActionMethod
参数(如果有)。它对 GET
和 POST
都这样做。这是有道理的。
在 ActionMethod
成功 运行 之后,使用提供的剃刀呈现视图,在我的例子中,它使用了尽可能多的 HtmlHelper
调用。到现在为止,您可能会想,'Yes I know how MVC works'。等一下,我马上就到了。
当我们使用例如@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 使用用户已经提供的值来呈现视图。文档还说明了这种行为,Whosebug 上有几篇文章证实了这一点。但它仅适用于 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
请求上传递给 actionmethod,因为 ModelState
字典包含一个名为 Id
.
的键
我的问题不是关于如何解决这个问题,因为这很简单:
- 将
ActionMethod
参数重命名为不同的名称
- 在使用
Modelstate.Clear()
返回 ActionResult
之前清除 ModelState
- 替换
ModelState
字典中的值,根本不向视图提供 Model
。
我的问题是:
为什么 GET
和 POST
的过程相同。在 GET
.
上使用填充的 ModelState
而不是提供的 Model
是否有任何有效的用例
DefaultModelBinder
不区分 GET 和 POST 请求。虽然只有 MVC 团队可以确认他们做出该决定的原因,但有一些有效的用例为什么应该在 GET 中使用 ModelState
(与在 POST 中使用的方式相同),因为您也可以提交GET 方法的表单。
以航空公司预订应用程序为例,您有一个模型具有 select 出发和到达地点、日期和乘客人数的属性(int
属性),加上过滤结果的集合 属性。 GET 方法签名可能类似于
public ActionResult Search(SearchViewModel model)
该视图包含一个使 GET 返回方法的视图,并包含一个用于显示乘客数量的文本框。
已禁用 javascript 的(不太聪明的)用户在文本框中输入 "TWO" 并提交表单。您的方法 returns 未填充可用航班集合的视图,因为 ModelState
无效(值 "TWO" 对 int
无效),现在用户看到错误消息说明 字段 Passengers 必须是数字。
如果使用模型值而不是 ModelState
值,则关联的文本框将显示 0
而不是 "Two"
,从而使错误消息令人困惑(但 0
是一个数字!- 我输入的内容发生了什么?)
当 MVC 运行 ActionMethod
时,它将填充 ModelState
字典并使用 ModelBinder
构建 ActionMethod
参数(如果有)。它对 GET
和 POST
都这样做。这是有道理的。
在 ActionMethod
成功 运行 之后,使用提供的剃刀呈现视图,在我的例子中,它使用了尽可能多的 HtmlHelper
调用。到现在为止,您可能会想,'Yes I know how MVC works'。等一下,我马上就到了。
当我们使用例如@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 使用用户已经提供的值来呈现视图。文档还说明了这种行为,Whosebug 上有几篇文章证实了这一点。但它仅适用于 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
请求上传递给 actionmethod,因为 ModelState
字典包含一个名为 Id
.
我的问题不是关于如何解决这个问题,因为这很简单:
- 将
ActionMethod
参数重命名为不同的名称 - 在使用
Modelstate.Clear()
返回 - 替换
ModelState
字典中的值,根本不向视图提供Model
。
ActionResult
之前清除 ModelState
我的问题是:
为什么 GET
和 POST
的过程相同。在 GET
.
ModelState
而不是提供的 Model
是否有任何有效的用例
DefaultModelBinder
不区分 GET 和 POST 请求。虽然只有 MVC 团队可以确认他们做出该决定的原因,但有一些有效的用例为什么应该在 GET 中使用 ModelState
(与在 POST 中使用的方式相同),因为您也可以提交GET 方法的表单。
以航空公司预订应用程序为例,您有一个模型具有 select 出发和到达地点、日期和乘客人数的属性(int
属性),加上过滤结果的集合 属性。 GET 方法签名可能类似于
public ActionResult Search(SearchViewModel model)
该视图包含一个使 GET 返回方法的视图,并包含一个用于显示乘客数量的文本框。
已禁用 javascript 的(不太聪明的)用户在文本框中输入 "TWO" 并提交表单。您的方法 returns 未填充可用航班集合的视图,因为 ModelState
无效(值 "TWO" 对 int
无效),现在用户看到错误消息说明 字段 Passengers 必须是数字。
如果使用模型值而不是 ModelState
值,则关联的文本框将显示 0
而不是 "Two"
,从而使错误消息令人困惑(但 0
是一个数字!- 我输入的内容发生了什么?)