在ASP.NET MVC中重定向后发布

时间:2009-10-14 17:41:22

标签: asp.net-mvc

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

  1. 用户前往/controller/index,要求他填写表格。
  2. 表单值已发布到/controller/calculate
  3. Calculate动作根据输入执行计算,并实例化包含操作结果的复杂对象。此对象存储在TempData中,用户被重定向到/controller/result
  4. /controller/resultTempData检索结果并将其呈现给用户。
  5. 此方法的问题在于,如果用户在查看/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)?

5 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

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

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

如果你能做到这一点,那么思考变得非常简单:你将计算所需的值哈希并将其用作缓存的密钥。如果用户刷新,他只会点击缓存。

如果你不能有一个有意义的网址,你可以发布到/ controller / index。如果用户点击F5,计算将再次开始,但使用哈希作为键的缓存将再次有用。

答案 2 :(得分:1)

TempData通常被认为对于将消息传递回用户而不是用于存储工作实体非常有用(用户刷新将会对TempData的内容进行核对)。

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

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

答案 3 :(得分:0)

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

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

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

我对这个设计并没有很好的调整,但我认为它可能有用。它不像TempData那样臭,你仍然可以坚持PRG模式。

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

答案 4 :(得分:0)

正如迈克尔所说,TempData有一个目的 - >仅存储一次旅行的物品,只存储一次旅行。据我了解,TempData实际上使用的是与您可能使用的相同的Session对象,但它会在下次旅行时自动从会话中删除该对象。

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