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

时间:2022-02-08 17:42:43

I am using the Redirect After Post pattern in my ASP.NET MVC application. I have the following scenario:

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

  1. User goes to /controller/index where he is asked to fill a form.
  2. 用户进入/ controller / index,要求他填写表格。

  3. Form values are POSTed to /controller/calculate.
  4. 表单值被POST到/ controller / calculate。

  5. The Calculate action performs calculation based on input and instantiates a complex object containing the results of the operation. This object is stored in TempData and user is redirected to /controller/result.
  6. Calculate操作基于输入执行计算,并实例化包含操作结果的复杂对象。此对象存储在TempData中,用户被重定向到/ controller / result。

  7. /controller/result retrieves the result from TempData and renders them to the user.
  8. / controller / result从TempData检索结果并将它们呈现给用户。

The problem with this approach is that if the user hits F5 while viewing the results in /controller/result the page can no longer be rendered as TempData has been expired and the result object is no longer available.

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

This behavior is not desired by the users. One possible solution would be instead of redirecting after the POST, just rendering the results view. Now if the user hits F5 he gets a browser dialog asking if he wants to repost the form. This also was not desired.

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

One possible solution I thought of was to serialize the result object and passing it in the URL before redirecting but AFAIK there are some limitations in the length of a GET request and if the object gets pretty big I might hit this limitation (especially if base64 encoded).

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

Another possibility would be to use the Session object instead of TempData to persist the results. But before implementing this solution I would like to know if there's a better way of doing it.

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


UPDATE:

Further investigating the issue I found out that if I re-put the result object in TempData inside the /controller/result action it actually works:

进一步研究这个问题我发现如果我将结果对象重新放在/ data / result动作中的TempData中,它实际上是有效的:

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

But this feels kind of dirty. Could there be any side effects with this approach (such as switching to out-of-process session providers as currently I use InProc)?

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

5 个解决方案

#1


5  

Store it in the Session with some unique key and pass the key as part of the url. Then as long as the session is alive they can use the back/forward button to their heart's content and still have the URL respond properly. Alternatively, you could use the ASP cache, but I'd normally reserve that for objects that are shared among users. Of course, if you used the parameters to the calculation as the key and you found the result in the cache, you could simply re-use it.

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

#2


2  

I think redirect after post makes much more sense when the resulting Url is meaningfull. In your case it would mean that all data required for the calculation is in the Url of /controller/result.

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

/controller/calculate would not do the calculation but /controller/result.

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

If you can get this done thinks get pretty easy: You hash the values required for the calculation and use it as the key for the cache. If the user refreshes he only hits the cache.

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

If you cant have a meaningfull url you could post to /controller/index. If the user hits F5 calculation would start again, but a cache with the hash as key would help again.

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

#3


1  

TempData is generally considered useful for passing messages back to the user not for storing working entities (a user refresh will nuke the contents of TempData).

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

I don't know of more appropriate place than the session to store this kind of information. I think the general idea is keep session as small as possible though. Personally I usually write some wrappers to add and remove specific objects to session. Cleaning them up manually where possible.

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

Alternatively you can store in a database in which you purge stale items on a regular basis.

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

#4


0  

I might adopt a similar idea to a lot of banks on their online banking sites by using one-time keys to verify all POSTs. You can integrate it into a html helper for forms and into your service layer (for example) for verification.

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

Let's say that you only want to post any instance of a form once. Add a guid to the form. If the form does not post back and the data is committed then you want to invalidate the guid and redirect to the GET action. If say the form was not valid, when the page posts back you need a new (valid) guid there in the form waiting for the next post attempt.

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

GUIDs are generated as required and added to a table in your DB. As they are invalidated (by POSTS, whether successful or not) they are flagged in the table. You may want to trim the table at 100 rows.. or 1000, depending on how heavy your app will be and how many rendered but not yet posted forms you may have at any one time.

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

I haven't really fine tuned this design but i think it might work. It wont be as smelly as the TempData and you can still adhere to the PRG pattern.

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

Remember, with PRG you dont want to send the new data to the GET action in a temp variable of some sort. You want to query it back from the data store, where it's now committed to.

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

#5


0  

As Michael stated, TempData has a single purpose -> store an object for one trip only and only one trip. As I understand it, TempData is essentially using the same Session object as you might use but it will automatically remove the object from the session on the next trip.

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

Stick with Session imho rather than push back in to TempData.

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

#1


5  

Store it in the Session with some unique key and pass the key as part of the url. Then as long as the session is alive they can use the back/forward button to their heart's content and still have the URL respond properly. Alternatively, you could use the ASP cache, but I'd normally reserve that for objects that are shared among users. Of course, if you used the parameters to the calculation as the key and you found the result in the cache, you could simply re-use it.

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

#2


2  

I think redirect after post makes much more sense when the resulting Url is meaningfull. In your case it would mean that all data required for the calculation is in the Url of /controller/result.

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

/controller/calculate would not do the calculation but /controller/result.

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

If you can get this done thinks get pretty easy: You hash the values required for the calculation and use it as the key for the cache. If the user refreshes he only hits the cache.

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

If you cant have a meaningfull url you could post to /controller/index. If the user hits F5 calculation would start again, but a cache with the hash as key would help again.

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

#3


1  

TempData is generally considered useful for passing messages back to the user not for storing working entities (a user refresh will nuke the contents of TempData).

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

I don't know of more appropriate place than the session to store this kind of information. I think the general idea is keep session as small as possible though. Personally I usually write some wrappers to add and remove specific objects to session. Cleaning them up manually where possible.

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

Alternatively you can store in a database in which you purge stale items on a regular basis.

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

#4


0  

I might adopt a similar idea to a lot of banks on their online banking sites by using one-time keys to verify all POSTs. You can integrate it into a html helper for forms and into your service layer (for example) for verification.

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

Let's say that you only want to post any instance of a form once. Add a guid to the form. If the form does not post back and the data is committed then you want to invalidate the guid and redirect to the GET action. If say the form was not valid, when the page posts back you need a new (valid) guid there in the form waiting for the next post attempt.

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

GUIDs are generated as required and added to a table in your DB. As they are invalidated (by POSTS, whether successful or not) they are flagged in the table. You may want to trim the table at 100 rows.. or 1000, depending on how heavy your app will be and how many rendered but not yet posted forms you may have at any one time.

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

I haven't really fine tuned this design but i think it might work. It wont be as smelly as the TempData and you can still adhere to the PRG pattern.

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

Remember, with PRG you dont want to send the new data to the GET action in a temp variable of some sort. You want to query it back from the data store, where it's now committed to.

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

#5


0  

As Michael stated, TempData has a single purpose -> store an object for one trip only and only one trip. As I understand it, TempData is essentially using the same Session object as you might use but it will automatically remove the object from the session on the next trip.

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

Stick with Session imho rather than push back in to TempData.

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