F5重复提交这点事儿, 我想每个人都会遇到……
这里分享的代码使用 filter 实现,利用 token 机制来防止重复提交。
当然要使用这个工具,首先你要在你项目的web.xml中配置好下面两个filter。其次,还需要此功能的 jsp 页面中加上这一段:
String clientToken = StringUtil.getToken(request);
还有,form中需要加上:
<input name="clientToken" type="hidden" value="<%=clientToken %>" />
这段代码的好处有二:
1> 解决了你的重复提交问题,
2> 做到了将 token 的功能和你后端的业务逻辑的解耦; 当然前端没有解耦。
~~~~~~仔细分析了一下,发现事实上, 做不到在前端解耦,因为 struts 也要求你在需要token功能的前端页面中加上<s: token /> 。这和web应用模型有关 -- 不能区分重复提交的request和正常提交的request,如果能, 也不需要这里的 token了, O(∩_∩)O~
下面来仔细的分析一把:
首先, 为什么F5会产生重复提交呢? 其实和提交后的request处理方式有关,如果你完成了数据处理后,使用了如下方式来跳转:
req.getRequestDispatcher(url).forward(req, resp);
就会在你F5的时候重复提交,因为这种跳转是在server端完成的,虽然跳转到了新的页面,但是并没有告知浏览器,于是当你F5的时候,再次带着你上次提交的所有的参数,提交到了相同的资源(通常是用来处理数据的逻辑代码,例如action),导致产生了重复提交。
那么 如何才能避免重复提交呢? 其实就一句话,帮助你的 action等处理单元 区分出重复提交的request, 我们这里使用token来给request打上标签^_^。如果我们发现那个request的标签过期了,那么我们就不做数据处理(例如添加一条评论等)具体做法:
1> 在提交之前准备好token,并更新server token (放在session中) 和 client token (放在页面中)
String clientToken = StringUtil.getToken(request);
同时,form中: <input name="clientToken" type="hidden" value="<%=clientToken %>" />
2>在有提交时进行检查:
在 TokenFilter中实现的。具体见代码。
搞技术不能闭门造车,小弟请大家多多指点,因为代码都贴上去了,比较冗长~~ 见谅,哈哈
这里分享的代码使用 filter 实现,利用 token 机制来防止重复提交。
当然要使用这个工具,首先你要在你项目的web.xml中配置好下面两个filter。其次,还需要此功能的 jsp 页面中加上这一段:
String clientToken = StringUtil.getToken(request);
还有,form中需要加上:
<input name="clientToken" type="hidden" value="<%=clientToken %>" />
这段代码的好处有二:
1> 解决了你的重复提交问题,
2> 做到了将 token 的功能和你后端的业务逻辑的解耦; 当然前端没有解耦。
~~~~~~仔细分析了一下,发现事实上, 做不到在前端解耦,因为 struts 也要求你在需要token功能的前端页面中加上<s: token /> 。这和web应用模型有关 -- 不能区分重复提交的request和正常提交的request,如果能, 也不需要这里的 token了, O(∩_∩)O~
下面来仔细的分析一把:
首先, 为什么F5会产生重复提交呢? 其实和提交后的request处理方式有关,如果你完成了数据处理后,使用了如下方式来跳转:
req.getRequestDispatcher(url).forward(req, resp);
就会在你F5的时候重复提交,因为这种跳转是在server端完成的,虽然跳转到了新的页面,但是并没有告知浏览器,于是当你F5的时候,再次带着你上次提交的所有的参数,提交到了相同的资源(通常是用来处理数据的逻辑代码,例如action),导致产生了重复提交。
那么 如何才能避免重复提交呢? 其实就一句话,帮助你的 action等处理单元 区分出重复提交的request, 我们这里使用token来给request打上标签^_^。如果我们发现那个request的标签过期了,那么我们就不做数据处理(例如添加一条评论等)具体做法:
1> 在提交之前准备好token,并更新server token (放在session中) 和 client token (放在页面中)
String clientToken = StringUtil.getToken(request);
同时,form中: <input name="clientToken" type="hidden" value="<%=clientToken %>" />
2>在有提交时进行检查:
在 TokenFilter中实现的。具体见代码。
搞技术不能闭门造车,小弟请大家多多指点,因为代码都贴上去了,比较冗长~~ 见谅,哈哈
标签: <无>
代码片段(3)
[代码] UriRecFilter.java, 用来记录提交前的url
01 | package com.tding.myject.filter; |
02 |
03 | import java.io.IOException; |
04 |
05 | import javax.servlet.*; |
06 | import javax.servlet.http.HttpServletRequest; |
07 |
08 | import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL; |
09 |
10 | public class UriRecFilter implements Filter { |
11 |
12 | @Override |
13 | public void destroy() { |
14 | // TODO Auto-generated method stub |
15 |
16 | } |
17 |
18 | @Override |
19 | public void doFilter(ServletRequest req, ServletResponse resp, |
20 | FilterChain chain) throws IOException, ServletException { |
21 | HttpServletRequest request = (HttpServletRequest) req; |
22 | request.getSession().setAttribute(REQUEST_SOURCE_URL, request.getRequestURI()); |
23 | chain.doFilter(req, resp); |
24 | } |
25 |
26 | @Override |
27 | public void init(FilterConfig arg0) throws ServletException { |
28 | // TODO Auto-generated method stub |
29 | System.out.println( "init UriRecFilter ..." ); |
30 | } |
31 |
32 | } |
[代码] TokenFilter.java, 实现令牌的分析处理
01 | package com.tding.myject.filter; |
02 |
03 | import java.io.IOException; |
04 |
05 | import javax.servlet.*; |
06 | import javax.servlet.http.*; |
07 |
08 | import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL; |
09 |
10 | public class TokenFilter implements Filter { |
11 |
12 | private HttpServletRequest request; |
13 | private HttpServletResponse response; |
14 |
15 | @Override |
16 | public void destroy() { |
17 | // TODO Auto-generated method stub |
18 |
19 | } |
20 |
21 | @Override |
22 | public void doFilter(ServletRequest req, ServletResponse resp, |
23 | FilterChain chain) throws IOException, ServletException { |
24 | // TODO Auto-generated method stub |
25 | request = (HttpServletRequest) req; |
26 | response = (HttpServletResponse) resp; |
27 |
28 | if (isTokenOverdue(request)) { |
29 | // 执行到此处说明令牌过期,即判断为重复提交 |
30 | request.getRequestDispatcher(getRequestSourceUri(request)).forward( |
31 | request, response); |
32 | } else { |
33 | chain.doFilter(req, resp); |
34 | } |
35 | } |
36 |
37 | @Override |
38 | public void init(FilterConfig arg0) throws ServletException { |
39 | System.out.println( "init TokenFilter ..." ); |
40 | } |
41 |
42 | private boolean isTokenOverdue(HttpServletRequest req) { |
43 |
44 | HttpSession session = req.getSession(); |
45 | String st = (String) session.getAttribute( "token" ); |
46 | // System.out.println("session Token: " + st); |
47 | String ct = req.getParameter( "clientToken" ); |
48 |
49 | return st != null && !st.equals(ct); |
50 | } |
51 |
52 | private String getRequestSourceUri(HttpServletRequest req) { |
53 |
54 | String rUri = (String) req.getSession() |
55 | .getAttribute(REQUEST_SOURCE_URL); |
56 | if (rUri == null ) |
57 | rUri = "/" ; |
58 | /** |
59 | * 最好不要放到这里,因为可能很多地方需要用到。 |
60 | */ |
61 | int clength = req.getContextPath().length(); |
62 | return clength > 1 ? rUri.substring(clength + 1 ) : "" ; |
63 | } |
64 | } |
[代码] StringUtil.java, 工具类
01 | package com.tding.myject.util; |
02 |
03 | import java.io.UnsupportedEncodingException; |
04 |
05 | import javax.servlet.http.HttpServletRequest; |
06 | import javax.servlet.http.HttpSession; |
07 |
08 | public class StringUtil { |
09 | public static String getParameter(HttpServletRequest req,String paraName) { |
10 | String param = null ; |
11 | try { |
12 | param = new String(req.getParameter(paraName).getBytes( "ISO-8859-1" ), "UTF-8" ); |
13 | } catch (UnsupportedEncodingException e) { |
14 | param = null ; |
15 | e.printStackTrace(); |
16 | } |
17 | return param; |
18 | } |
19 | |
20 | public static String getToken(HttpServletRequest req) { |
21 | HttpSession sessions = req.getSession(); |
22 | String token = StringUtil.generateToken(req); |
23 | req.setAttribute( "clientToken" , token); |
24 | sessions.setAttribute( "token" , token); |
25 | return token; |
26 | } |
27 | |
28 | public static String generateToken(HttpServletRequest req) { |
29 | return req.getSession().getId() + System.currentTimeMillis(); |
30 | } |
31 | } |