解决重复提交的三种方案!

struts应用中重复提交的问题的三种解决方案

例如增加一个用户
1、在增加前的action (actionA)中执行一个方法:saveToken(request)。然后转到页面。
2、当页面的数据填写好,提交的action (actionB)。action在插入数据前首先调用用方法:isTokenValid(request)来判断。
  如果为true:则执行resetToken(request);如果为false:则执行saveToken(request)。
原理说明:
a)当actionA执行saveToken(request)后,会在session范围中创建名字为org.apache.struts.action.TOKEN对象,该对象的值根据sessionid和当前的时间生成一个唯一的值。转到的页面中的form中增加<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="cfe992d0d493308117a1a7ff3841d42c">  语句。value值是唯一的与在actionA创建的值是一样的。
b)当actionB执行时,isTokenValid(request)判断提交的value与session中的值是否相等。
   若相等,删除则org.apache.struts.action.TOKEN对象resetToken(request),然后进行下一步操作。
   若不等,建立一个新的org.apache.struts.action.TOKEN对象saveToken(request),返回错误页面。


1, javascript ,设置一个变量,只允许提交一次。 

<script language="javascript"> 
   var checkSubmitFlg = false; 
   function checkSubmit() { 
   if (checkSubmitFlg == true) { 
   return false; 
   } 
   checkSubmitFlg = true; 
   return true; 
   } 
   document.ondblclick = function docondblclick() { 
   window.event.returnValue = false; 
   } 
   document.onclick = function doconclick() { 
   if (checkSubmitFlg) { 
   window.event.returnValue = false; 
   } 
   } 
  </script> 
   
  <html:form action="" method="post" οnsubmit="return checkSubmit();">

 
2, 还是javascript,将提交按钮或者image置为disable  
 

<html:form action="" method="post" 
   οnsubmit="getElById('submitInput').disabled = true; return true;">    
   <html:image styleId="submitInput" src="images/ok_b.gif" border="0" />  
   </html:form>

3, 利用struts的同步令牌机制  
  利用同步令牌(Token)机制来解决Web应用中重复提交的问题,Struts也给出了一个参考实现。
  基本原理: 
  服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。 
 

if (isTokenValid(request, true)) { 
   // your code here 
   return mapping.findForward("success"); 
  } else { 
   saveToken(request); 
   return mapping.findForward("submitagain"); 
  }


  Struts根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌的,具体实现可以参考TokenProcessor类中的generateToken()方法。  
  1. //验证事务控制令牌,<html:form >会自动根据session中标识生成一个隐含input代表令牌,防止两次提交
  2. 在action中:  
  

//<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" 
   // value="6aa35341f25184fd996c4c918255c3ae"> 
   if (!isTokenValid(request)) 
errors.add(ActionErrors.GLOBAL_ERROR, 
   new ActionError("error.transaction.token")); 
   resetToken(request); //删除session中的令牌

 
  3. action有这样的一个方法生成令牌  
 

protected String generateToken(HttpServletRequest request) {   
   HttpSession session = request.getSession(); 
   try { 
   byte id[] = session.getId().getBytes(); 
   byte now[] = 
   new Long(System.currentTimeMillis()).toString().getBytes(); 
   MessageDigest md = MessageDigest.getInstance("MD5"); 
   md.update(id); 
   md.update(now); 
   return (toHex(md.digest())); 
   } catch (IllegalStateException e) { 
   return (null); 
   } catch (NoSuchAlgorithmException e) { 
   return (null); 
   } 
   }



在更新的时候防止按钮重复点击,主要是用Session来做判断
在JSP/Servlet中可以
JSP页面

<input type="hidden " name=" <% =com.lims.util.SynchroToken.TOKEN_NAME%> " value =" <%= com.lims.util.SynchroToken.getToken(request)%>" >  

SynchroToken.java 
package com.lims.util; 
import org.apache.struts.util.*; 
import javax.servlet.http.*; 
import javax.servlet.jsp.*; 
import org.apache.struts.action.*; 
/** 
  * <p>Title: SynchroToken </p> 
  * <p>Description: </p> 
  * <p>Copyright: Copyright (c) 2004</p> 
  * <p>Company: NetStar</p> 
  * @author Jstar 
  * @version 1.0 
  * Created in 2004/04/21 
  */ 
public class SynchroToken{ 
public final static java.lang.String TOKEN_NAME = "_token"; 
public static boolean checkToken (HttpServletRequest request){ 
   boolean isEqual = false; 
   HttpSession session = request.getSession (); 
   String formToken = request.getParameter (TOKEN_NAME); 
   String sessionToken = (String)session.getAttribute (TOKEN_NAME); 
   System.out.println ("formToken: " + formToken + " sessionToken: " + 
                       sessionToken); 
   if (formToken != null && sessionToken == null){ 
     session.setAttribute (TOKEN_NAME, formToken); 
     isEqual = true; 
   } 
   return isEqual; 
} 
/** 
  * Insert the method's description here. 
  * Creation date: (4/19/2004 3:23:25 PM) 
  * @return java.lang.String 
  * @param request javax.servlet.http.HttpServletRequest 
  */ 
public static String getToken (HttpServletRequest request){ 
   String token = "" + System.currentTimeMillis (); 
   HttpSession session = request.getSession (); 
   if (session != null){ 
     session.removeAttribute (TOKEN_NAME); 
   } 
   return token;z 
} 
/** 
  * Insert the method's description here. 
  * Creation date: (4/19/2004 3:24:10 PM) 
  * @return java.lang.String 
  */ 
final static java.lang.String getTOKEN_NAME (){ 
   return TOKEN_NAME; 
} 
public static String message (PageContext pageContext, String key) throws 
     JspException{ 
   return RequestUtils.message (pageContext, null, null, key); 
} 
}


---------------------------------------------------------------------------------------------------------------------------------------------------
用java中的struts2.0前台jsp页面每次刷新,都有重复提交;我提交按钮用的是submit
 


解决方案:


1、 通过重定向的方式:把地址栏的地址显示为最后的地址。
2、 禁止客户端刷新:通过JS来控制,禁用刷新的功能、清除访问历史纪录;
3、 Struts自带的方式(令牌):服务器端发放一次性使用的令牌方式,服务器端给客户端每次请求发一个令牌,在客户端提交之后,检查令牌,如果这个令牌是第一次使用,就ok;否则,提示重复提交。
Action中有个几个相关方法:
SaveToken(request):生成一个新的令牌,并保存到客户端的请求对象中。
IsTokenValid(request,true):检查客户端的令牌是否是第一次使用,并且使该令牌失效。
ResetToken(request):重置客户端的令牌。