【问题标题】:MVC 3 Value Lost After Sent to Client发送给客户端后 MVC 3 价值丢失
【发布时间】:2011-08-04 13:32:04
【问题描述】:

我一定错过了它的工作原理,因为我无法弄清楚。也许这里有人可以提供帮助。

我的地址对象有一个名为 ValidationStatus 的属性。它在屏幕上不可见,但有一个隐藏字段:

@Html.HiddenFor(model => model.ValidationStatus)

所以,我运行程序,打开一个现有地址,其 ValidationStatus 为“OK”,然后更改地址使其无效。然后我将表单发布到控制器。对象的 Validate 方法调用第 3 方服务,该服务返回错误。代码将 ValidationStatus 设置为“无效”并返回带有验证消息的视图。

当视图加载时,ValidationStatus 被正确设置为“无效”,我可以通过在视图中调试以下语句看到:

@if (Model.ValidationStatus == "Invalid") //show an additional field.

所以我在新字段中输入数据并再次将表单发布到控制器。在控制器的第一行中,我放置了一个断点并在即时窗口中检查 collection["ValidationStatus"]。它是“OK”而不是“Invalid”。

我在这里缺少什么?为什么价值没有坚持?客户端没有任何东西可以改变这个值。

这是控制器代码(非常基本,真的):

[HttpPost]
public ActionResult Index(FormCollection collection, string destinationControllerName)
{
    PrepareSecondaryData(); // loads drop-down lists in case the View needs to be returned

    try
    {
        if (!TryUpdateModel(_policy))
            return View(_policy);

        if (!_services.PolicyEditor.SavePolicy(_policy))
            return View(_policy);
    }
    catch (Exception exp)
    {
        UIHelper.Log(UIHelper.LogLevel.Error, this, "Error during Save", exp);
        ViewBag.Error = UIHelper.GenericErrorMessage();
        return View(_policy);
    }

    return RedirectToAction("Index", destinationControllerName);
}

【问题讨论】:

  • 似乎问题出在您的控制器代码中,但如果您不发布它,我无法帮助您。请添加该过程中涉及的所有操作方法和任何私有方法。
  • 好的,已添加。我把它省略了,因为里面没有太多东西。 TryUpdateModel() 调用对象的 Validate 方法,该方法完成所有工作。底线是 Validate() 使用新值填充模型的 ValidationStatus,并且由于 TryUpdateModel() 将返回 false,因此 View 将返回给客户端。
  • 在您编辑后添加了一个可能太长的答案:)

标签: asp.net-mvc asp.net-mvc-3


【解决方案1】:

在向客户端渲染视图时,ModelState 在提供模型值方面具有最高优先级。你的情况就是这样。当视图第一次发送给客户端时,ValidationStatus 对应的ModelState["ValidationStatus"] 没有得到值,所以它取模型的值 - “OK”。当它发布到服务器时,ModelState["ValidationStatus"] 会填充“OK” - 从客户端的隐藏字段发送。并且当被第三方验证并再次返回时,即使model.ValidationStatus == "Invalid"ModelState["ValidationStatus"] == "OK",因此根据后者的更高优先级,ModelState 为模型设置值“OK”。并且客户端在隐藏字段中获得值“OK”。要修复它,请执行以下操作

  ModelState["ValidationStatus"].Value = new ValueProviderResult("Invalid", "Invalid", CultureInfo.CurrentCulture);

一般的想法是ModelState数组中的对应记录应该具有模型的正确值。

更新:

或者,从模型状态中清除价值,以使 MVC 使用模型中的价值。 ModelState.Remove("ValidationStatus")

【讨论】:

    【解决方案2】:

    您发布的代码比您想象的要多:)

    首先,您的代码在您呈现视图时并不总是相同的。第一次,你会来自这种方法,我想:

    [HttpGet]
    public ActionResult Index()
    

    注意HttpGet。您没有包含此方法的代码,但我认为您将获取要显示的数据,将其传递给 ViewData.Model,然后渲染视图。

    在第一次发帖后,这个方法完全被忽略了。如果出现错误,您将直接从您的 HttpPost 方法渲染视图,这是一个非常严重的设计缺陷。它不会直接影响您的问题,但解决此问题将为您提供工作代码和更好的整体设计。

    作为前提,我建议您在 Google 上搜索 PRG PATTERN,然后继续阅读此处。

    一段时间后……

    既然您是 PRG PATTERN 的专家和忠实粉丝(我相信您会喜欢它),我将给您一些关于如何实施它的提示以及您遇到的常见问题可能会遇到。

    首先,您的HttpGet 方法不会改变。这是为什么? @archil 已经在这里给出了答案,但无论如何我都会解释一下。 ModelState 中包含的值优先于 ViewData.Model。这样做是为了在重新显示表单时保留用户输入。

    我知道你接下来会想到什么。使用 PRG 模式,执行重定向时会丢失 ModelState。确实如此。这就是为什么 MvcContrib 的人做了这个神奇的 ActionFilter 来让一切正常工作。 ModelStateToTempData 属性,当应用于 POST 方法,甚至应用于整个控制器时,将使 POST 方法中的任何 ModelState 在 TempData 中序列化并在之后调用的第一个 GET 方法中反序列化。

    因此您将拥有:POST -> 处理数据/错误 -> ModelState 的序列化 -> 重定向到 GET 方法以显示表单 -> 将 TempData 反序列化回 ModelState -> 利润。

    斜体是ModelStateToTempData 属性自动执行的内容。

    我敢肯定,在您按照 PRG PATTERN 重构您的代码后,您的错误就会消失,并且您会为您的集合添加一个非常有用的技能,并防止那些讨厌的“表单重新提交”消息会碰巧以设计不佳的形式相遇。

    希望对你有所帮助。

    【讨论】:

    • 对不起,我直到现在才看到这个。非常感谢您的详细回复;我会调查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2018-10-27
    • 2011-01-19
    • 2016-03-06
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    相关资源
    最近更新 更多