java 你总是在 POST 后重定向吗?如果是,您如何管理?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1083357/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do you always REDIRECT after POST? If yes, How do you manage it?
提问by Jay
Say, you are submitting a form, which affects your database (adding records/ deleting them/ updating them) and this is how your request looks like:
假设您正在提交一个表单,这会影响您的数据库(添加记录/删除它们/更新它们),这就是您的请求的样子:
POST /application/action=update
POST /应用程序/操作=更新
Now, say, you are done with your update, so you would like to take the user to the home page.
现在,假设您已完成更新,因此您希望将用户带到主页。
Response.sendRedirect /application/action=home
Response.sendRedirect /application/action=home
This works wonderfully well. User is sent a redirect after POST, so even if the user tries to refresh the page by hitting F5, you are good. However, this will not work if you did this:
这非常有效。用户在 POST 后被发送重定向,因此即使用户尝试通过按 F5 刷新页面,您也很好。但是,如果您这样做,这将不起作用:
requestDispatcher.forward(/application/action=home)
requestDispatcher.forward(/application/action=home)
Given that there is a scenario where you have to display different kinds of error / success messages after you are done with your update, you are most likely doing a forward after POST. In such a scenario, how do you avoid update actions from happening twice?
鉴于在完成更新后您必须显示不同类型的错误/成功消息的情况,您很可能在 POST 后进行转发。在这种情况下,您如何避免更新操作发生两次?
I find it rather amusing that many secure sites (banks) / payment gateways tend to inform the user by placing text on screen, such as "Please don't press back / refresh buttons".
我觉得很有趣的是,许多安全站点(银行)/支付网关倾向于通过在屏幕上放置文本来通知用户,例如“请不要按后退/刷新按钮”。
Is there no better way to handling this? Other than requesting the user not to press these buttons? When I last checked, there was something called the 'Vertical Response Cache'. A Filter that would identify uniqueness of your request in a session and tries to send a cached response if the request is duplicate. Are there any simpler ways to solving this classic problem?
有没有更好的方法来处理这个问题?除了要求用户不要按下这些按钮?当我上次检查时,有一个叫做“垂直响应缓存”的东西。一个过滤器,用于识别会话中请求的唯一性,并在请求重复时尝试发送缓存响应。有没有更简单的方法来解决这个经典问题?
Here is a link to the vertical response cache solution I was talking about: http://www.fingo.info/en/articles/_1.html. I am, However, not sure as to how well this really works.
这是我正在谈论的垂直响应缓存解决方案的链接:http: //www.fingo.info/en/articles/_1.html。但是,我不确定这真的有效。
采纳答案by David Z
One thought that I've had is to embed a unique ID (probably a random string) as a hidden form field in the form that is being POST-submitted. The ID string can be put in the database as a "transaction ID". Now, when you go to update the database, first check whether there's an existing record with the submitted transaction ID, and if so, assume it's a duplicate and don't change the database.
我的一个想法是在 POST 提交的表单中嵌入一个唯一的 ID(可能是一个随机字符串)作为隐藏的表单字段。ID 字符串可以作为“事务 ID”放入数据库中。现在,当您去更新数据库时,首先检查是否有提交的事务ID 的现有记录,如果是,则假设它是重复的并且不要更改数据库。
Of course, as I said, this is just a thought. I don't know what methods are actually used in practice. (I suspect that a lot of less-critical sites just ignore the problem and hope their users will be smart... a losing proposition if I ever saw one ;-)
当然,正如我所说,这只是一个想法。我不知道在实践中实际使用了哪些方法。(我怀疑很多不太重要的网站只是忽略了这个问题,并希望他们的用户是聪明的……如果我看到一个的话,这是一个失败的提议;-)
EDIT: as pointed out in the comments, storing transaction IDs in the database might take up a lot of space, but if that's an issue, you could keep an in-memory cache of all transaction IDs processed in the last 5 minutes/1 hour/1 day/whatever. That should work unless you're up against a determined hacker...
编辑:正如评论中所指出的,在数据库中存储事务 ID 可能会占用大量空间,但如果这是一个问题,您可以保留过去 5 分钟/1 小时内处理的所有事务 ID 的内存缓存/1 天/随便什么。除非你遇到一个坚定的黑客,否则这应该有效......
回答by Laurence Gonsalves
Yes, I believe that you should redirect after a POST, with the exception of API requests. Without doing this not only do you have to worry about getting duplicate POSTs when the user uses the back button, but the browser will also give the user annoying dialogs when they try to use the back button.
是的,我相信您应该在 POST 后重定向,API 请求除外。如果不这样做,您不仅要担心在用户使用后退按钮时会收到重复的 POST,而且当用户尝试使用后退按钮时,浏览器还会给用户带来烦人的对话框。
Response.sendRedirect works in practice, but tecnically speaking this is sending the wrong HTTP response code for this purpose. sendRedirect sends a 302, but the correct code to use to transform a POST into a GET is 303. (most browsers will treat a 302 just like a 303 if they get it in response to a POST, however)
Response.sendRedirect 在实践中起作用,但从技术上讲,这是为此目的发送错误的 HTTP 响应代码。sendRedirect 发送 302,但用于将 POST 转换为 GET 的正确代码是 303。(但是,如果大多数浏览器在响应 POST 时收到 302,则它们会将 302 视为 303)
In general you want the redirect to send the user to whatever view will display the effect of their change. For example, if they edit a widget, they should be redirected to the view of that widget. If they delete a widget, they should be redirected to the view that the widget would have appeared in when it existed (perhaps the widget list).
通常,您希望重定向将用户发送到将显示其更改效果的任何视图。例如,如果他们编辑一个小部件,他们应该被重定向到该小部件的视图。如果他们删除了一个小部件,他们应该被重定向到小部件存在时会出现的视图(可能是小部件列表)。
Sometimes it's nice to have a status message to further drive home the fact that an action occurred. A simple way to do this is to have a common parameter to your views that, when set, will display an action completed message. eg:
有时,有一条状态消息可以进一步说明发生了某个操作的事实,这很好。一个简单的方法是为您的视图设置一个公共参数,当设置时,将显示操作完成消息。例如:
/widget?id=12345&msg=Widget+modified.
Here the "msg" parameter contains the message "Widget modified". The one downside to this approach is that it may be possible for malicious sites to give your users confusing/misleading messages. eg:
这里的“msg”参数包含消息“Widget modified”。这种方法的一个缺点是恶意站点可能会给您的用户提供令人困惑/误导性的消息。例如:
/account?msg=Foo+Corp.+hates+you.
If you're really worried about this you could include an expiring signature for the message as an additional parameter. If the signature is invalid or has expired, simply don't display the message.
如果您真的担心这一点,您可以将消息的到期签名作为附加参数包含在内。如果签名无效或已过期,请不要显示该消息。
回答by satish
The best solution to solve the problem of showing status messages to the users after a POST to GET redirect is to use user sessions.
解决在 POST 到 GET 重定向后向用户显示状态消息的问题的最佳解决方案是使用用户会话。
How
如何
Add attributes to user session with value as set of messages to be displayed. for eg.
将属性添加到用户会话,其值为要显示的消息集。例如。
userSession.put("success_messages", new HashSet<String>(){"Success", "Check your account balance"});
userSession.put("warning_messages", new HashSet<String>(){"Your account balance is low. Recharge immediately"});
And have a filter which scans the user session for these particular attributes and outputs the messages. The filter should delete the attributes after reading once, as the status messages are generally displayed only once.
并有一个过滤器来扫描用户会话中的这些特定属性并输出消息。过滤器应该在读取一次后删除属性,因为状态消息通常只显示一次。
回答by Ali Abdel-Aziz
I find it rather amusing that many secure sites (banks) / payment gateways tend to inform the user by placing text on screen, such as "Please don't press back / refresh buttons".
我觉得很有趣的是,许多安全站点(银行)/支付网关倾向于通过在屏幕上放置文本来通知用户,例如“请不要按后退/刷新按钮”。
some people find its better to "disable all Back, Refresh event on this critical pages"; I'm not sure if this is good or not.
有些人发现“禁用此关键页面上的所有返回、刷新事件”更好;我不确定这是否好。
But your addressed solution "vertical response cache" sounds nice
但是您提出的解决方案“垂直响应缓存”听起来不错
回答by Pat
Its a little non-obvious but:
它有点不明显,但是:
- create a keyed-object in the user session.
- the value is a Request + java Future for the result
- return immediately with a client-side redirect.
- while the client-side redirect is being handled, have a worker thread work on producing the answer.
- 在用户会话中创建一个键控对象。
- 该值是结果的 Request + java Future
- 使用客户端重定向立即返回。
- 在处理客户端重定向时,让工作线程处理生成答案。
So by the time the client browser completes the redirect, getting the new page's images, etc... the results are waiting for the user.
因此,当客户端浏览器完成重定向、获取新页面的图像等时……结果正在等待用户。
The alternative is to make the user painfully aware of how long the database is taking.
另一种方法是让用户痛苦地意识到数据库需要多长时间。
Security Update (2011 Jan 24 ) :
安全更新(2011 年 1 月 24 日):
The key is vulnerable to attack since it is part of the response to the client, so
密钥很容易受到攻击,因为它是对客户端的响应的一部分,所以
- Generate a random key
- Use user's session id as a salt to create a SHA-1
- Store both the random key and the SHA-1 in the database with (, ) as the primary key. (no separate indexing on the RANDOMKEY.
- Use both RANDOMKEY and the SHA-1 as the db lookup.
- Do not store the Session Id (avoid privacy issues with being able to corollate many entries to the same user)
- Expire the results after 2-3 days. ( Allows a daily batch job to do the clean up and avoids creating problems for user sessions that are semi-long lasting )
- 生成随机密钥
- 使用用户的会话 ID 作为盐创建 SHA-1
- 将随机密钥和 SHA-1 都存储在数据库中,以 (, ) 作为主键。(在 RANDOMKEY 上没有单独的索引。
- 使用 RANDOMKEY 和 SHA-1 作为数据库查找。
- 不要存储会话 ID(避免将多个条目关联到同一用户的隐私问题)
- 2-3 天后使结果过期。(允许每日批处理作业进行清理并避免为半持久的用户会话造成问题)
This method requires any hacker to know both the session id and the random key.
这种方法要求任何黑客都知道会话 ID 和随机密钥。
This approach may seem overkill, but a redirect-hardened mechanism can be used for situations like password resets.
这种方法似乎有点矫枉过正,但重定向强化机制可用于密码重置等情况。
回答by Dead Programmer
If you are working with java server side scripting and also using struts 2 then you refer this link which talks about on using token .
如果您正在使用 java 服务器端脚本并使用 struts 2,那么您可以参考此链接,该链接讨论了使用 token 。
http://www.xinotes.org/notes/note/369/
http://www.xinotes.org/notes/note/369/
A token should be generated and kept in session for the initial page render, when the request is submitted along with the token for the first time , in struts action run a thread with thread name as the token id and run the logic whatever the client has requested for , when client submit again the same request, check whether the thread is still running(thread.getcurrentthread().interrupted) if still running then send a client redirect 503.
应该为初始页面呈现生成一个令牌并保存在会话中,当第一次提交请求和令牌时,在 struts 操作中,运行一个线程名作为令牌 ID 的线程,并运行客户端拥有的任何逻辑请求,当客户端再次提交相同的请求时,检查线程是否仍在运行(thread.getcurrentthread().interrupted)如果仍在运行则发送客户端重定向503。
Please look at the ExecuteAndWaitInterceptor of struts 2code, the logic of this combined with token will help out fast click
请看struts 2code的ExecuteAndWaitInterceptor,这个结合token的逻辑有助于快速点击

