【问题标题】:Redirect After Post in ASP.NET MVC在 ASP.NET MVC 中发布后重定向
【发布时间】:2010-12-06 18:34:39
【问题描述】:

我在我的 ASP.NET MVC 应用程序中使用Redirect After Post 模式。我有 以下场景:

  1. 用户转到/controller/index,要求他填写表格。
  2. 表单值发布到/controller/calculate
  3. Calculate 操作根据输入执行计算,并实例化一个包含操作结果的复杂对象。该对象存储在TempData,用户被重定向到/controller/result
  4. /controller/resultTempData 检索结果并将其呈现给用户。

这种方法的问题是,如果用户在查看/controller/result 中的结果时按 F5,则无法再呈现页面,因为TempData 已过期并且结果对象不再可用。

用户不希望这种行为。一种可能的解决方案是在 POST 之后不进行重定向,而只是呈现结果视图。现在,如果用户按 F5,他会看到一个浏览器对话框,询问他是否要重新发布表单。这也是不希望的。

我想到的一个可能的解决方案是在重定向之前序列化结果对象并在 URL 中传递它,但是 AFAIK GET 请求的长度有一些限制,如果对象变得相当大,我可能会遇到这个限制(尤其是如果 base64 编码)。

另一种可能性是使用Session 对象而不是TempData 来保存结果。但在实施此解决方案之前,我想知道是否有更好的方法。


更新:

进一步调查该问题,我发现如果我将结果对象重新放入 TempData 中的 /controller/result 操作中,它实际上可以工作:

public ActionResult Result()
{
    var result = TempData["result"];
    TempData["result"] = result;
    return View(result);
}

但这感觉有点脏。这种方法是否有任何副作用(例如切换到进程外会话提供程序,因为我目前使用 InProc)?

【问题讨论】:

  • 当你说“重定向”时,你是在调用 RedirectToAction 吗?

标签: asp.net-mvc


【解决方案1】:

使用一些唯一的密钥将其存储在 Session 中,并将密钥作为 url 的一部分传递。然后,只要会话处于活动状态,他们就可以使用后退/前进按钮来访问他们的内容,并且仍然让 URL 正确响应。或者,您可以使用 ASP 缓存,但我通常会将其保留给用户之间共享的对象。当然,如果你使用计算的参数作为key,并且在缓存中找到结果,你可以简单地重复使用它。

【讨论】:

  • 谢谢,这是一个很好的建议。我会尝试使用在 url 中传递的唯一键来实现它。
【解决方案2】:

我认为,当生成的 Url 有意义时,帖子后重定向更有意义。 在您的情况下,这意味着计算所需的所有数据都在 /controller/result 的 URL 中。

/controller/calculate 不会进行计算,而是 /controller/result。

如果你能做到这一点,认为会很容易:你散列计算所需的值并将其用作缓存的键。如果用户刷新,他只会命中缓存。

如果你不能有一个有意义的 url,你可以发布到 /controller/index.如果用户按 F5,计算将重新开始,但以哈希为键的缓存将再次提供帮助。

【讨论】:

  • 我需要在计算之前验证输入,如果出现错误,则显示相同的表单,其中预填充的值和错误字段标记为红色。如果在 Result 操作中完成计算,我将如何处理 ModelState 错误?我是否应该重定向回 Index 操作并将所有输入值和验证错误再次放入 TempData 以便我可以正确显示表单?
  • 在结果中更难验证。我会使用经过验证的值重定向到它。最简单的方法是发布到索引并验证数据/显示错误。
【解决方案3】:

TempData 通常被认为对于将消息传回给用户而不是存储工作实体很有用(用户刷新会破坏 TempData 的内容)。

我不知道比会话更合适的地方来存储这种信息。我认为一般的想法是保持会话尽可能小。就我个人而言,我通常会编写一些包装器来添加和删除会话中的特定对象。尽可能手动清理它们。

或者,您可以存储在数据库中,定期在其中清除过时的项目。

【讨论】:

    【解决方案4】:

    我可能会在许多银行的网上银行网站上采用类似的想法,即使用一次性密钥来验证所有 POST。您可以将其集成到表单的 html 帮助程序中,并集成到您的服务层(例如)中以进行验证。

    假设您只想发布表单的任何实例一次。向表单添加指南。如果表单没有回发并且数据已提交,那么您希望使 guid 无效并重定向到 GET 操作。如果说表单无效,当页面回发时,您需要一个新的(有效的)guid 在表单中等待下一次发布尝试。

    GUID 会根据需要生成并添加到数据库中的表中。由于它们被无效(通过 POST,无论成功与否),它们在表中被标记。您可能希望将表格修剪为 100 行或 1000 行,具体取决于您的应用程序的重量以及您在任何时候可能有多少已呈现但尚未发布的表单。

    我并没有真正微调这个设计,但我认为它可能会起作用。它不会像 TempData 那样臭,你仍然可以坚持 PRG 模式。

    请记住,使用 PRG,您不想将新数据发送到某种临时变量中的 GET 操作。您想从数据存储中查询它,它现在已提交到该位置。

    【讨论】:

    • 当前应用程序没有数据存储。它在内存中执行所有工作(只有计算,没有持久性)。这就是我考虑使用 TempData 或 Session 作为临时数据存储的原因。
    【解决方案5】:

    正如 Michael 所说,TempData 有一个单一的目的 -> 存储一个对象仅用于一次行程且仅一次行程。据我了解,TempData 本质上使用的 Session 对象与您可能使用的对象相同,但它会在下次旅行时自动从会话中删除该对象。

    坚持使用 Session imho,而不是推回 TempData。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-02
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多