防止 F5 重复提交

本文转载自http://www.oschina.net/code/snippet_78857_6953

这里分享的代码使用 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中实现的。具体见代码。
UriRecFilter.java, 用来记录提交前的url

package com.tding.myject.filter;  
  
import java.io.IOException;  
  
import javax.servlet.*;  
import javax.servlet.http.HttpServletRequest;  
  
import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL;  
  
public class UriRecFilter implements Filter {  
  
   @Override 
   public void destroy() {  
       // TODO Auto-generated method stub  
  
   }  
  
   @Override 
   public void doFilter(ServletRequest req, ServletResponse resp,  
           FilterChain chain) throws IOException, ServletException {  
       HttpServletRequest request = (HttpServletRequest) req;  
       request.getSession().setAttribute(REQUEST_SOURCE_URL, request.getRequestURI());  
       chain.doFilter(req, resp);  
   }  
  
   @Override 
   public void init(FilterConfig arg0) throws ServletException {  
       // TODO Auto-generated method stub  
               System.out.println("init UriRecFilter ...");  
   }
} 


TokenFilter.java, 实现令牌的分析处理

package com.tding.myject.filter;  
  
import java.io.IOException;  
  
import javax.servlet.*;  
import javax.servlet.http.*;  
  
import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL;  
  
public class TokenFilter implements Filter {  
  
   private HttpServletRequest request;  
   private HttpServletResponse response;  
  
   @Override 
   public void destroy() {  
       // TODO Auto-generated method stub  
  
   }  
  
   @Override 
   public void doFilter(ServletRequest req, ServletResponse resp,  
           FilterChain chain) throws IOException, ServletException {  
       // TODO Auto-generated method stub  
       request = (HttpServletRequest) req;  
       response = (HttpServletResponse) resp;  
  
       if (isTokenOverdue(request)) {  
           // 执行到此处说明令牌过期,即判断为重复提交  
           request.getRequestDispatcher(getRequestSourceUri(request)).forward(  
                   request, response);  
       } else {  
           chain.doFilter(req, resp);  
       }  
   }  
  
   @Override 
   public void init(FilterConfig arg0) throws ServletException {  
       System.out.println("init TokenFilter ...");  
   }  
  
   private boolean isTokenOverdue(HttpServletRequest req) {  
  
       HttpSession session = req.getSession();  
       String st = (String) session.getAttribute("token");  
       // System.out.println("session Token: " + st);  
       String ct = req.getParameter("clientToken");  
  
       return st != null && !st.equals(ct);  
   }  
  
   private String getRequestSourceUri(HttpServletRequest req) {  
  
       String rUri = (String) req.getSession()  
               .getAttribute(REQUEST_SOURCE_URL);  
       if (rUri == null)  
           rUri = "/";  
       /**  
        * 最好不要放到这里,因为可能很多地方需要用到。  
        */ 
       int clength = req.getContextPath().length();  
       return clength > 1 ? rUri.substring(clength + 1) : "";  
   }  
}


StringUtil.java, 工具类

package com.tding.myject.util;  
  
import java.io.UnsupportedEncodingException;  
  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpSession;  
  
public class StringUtil {  
   public static String getParameter(HttpServletRequest req,String paraName) {  
       String param = null;  
       try {  
           param = new String(req.getParameter(paraName).getBytes("ISO-8859-1"), "UTF-8");  
       } catch (UnsupportedEncodingException e) {  
           param = null;  
           e.printStackTrace();  
       }  
       return param;  
   }  
      
   public static String getToken(HttpServletRequest req) {  
       HttpSession sessions = req.getSession();  
       String token = StringUtil.generateToken(req);  
       req.setAttribute("clientToken", token);  
       sessions.setAttribute("token", token);  
       return token;  
   }  
      
   public static String generateToken(HttpServletRequest req) {  
       return req.getSession().getId() + System.currentTimeMillis();  
   }  
}


 

你可能感兴趣的:(防止 F5 重复提交)