http://www.4ucode.com/Study/Topic/1050065
在开发中,如果一个新增或修改的表单,在后台完成数据库操作后我们设定的不是跳转到其他页面,还是返回本页面,这时点击浏览器的后退再提交或刷新页面,会导致form表单重复提交,即这条记录会被增加或修改两次。
导致表单重复提交的原因是:第一次提交的表单会被缓存到内存中,直到页面下次提交或页面关闭或转向其他页面时才消失。在自调用返回时,内存中的数据依然在,这时页面中的判断提交的代码依然可以检测到提交的值,顾会产生重复提交的效果。
总结网上的解决办法和自己的测试,可以用以下几个办法解决:
(1)最简单:页面提交后转到另一个页面而不是本页面
(2)提交表单后提交按钮变灰/隐藏提交按钮
(3)在js里设置全局变量,提交后修改该变量的值,依据变量的值判断是否重复提交
var flag=false;
function checkForm(){
if (flag==true){
return false;
}
flag=true;
document.form1.submit();
}
(4)在登录用户的Session中,放置一个随机数作为同步标记,并在初始化新增或编辑页面时就返回到前台页面中,用隐藏域赋值,当form被提交时,form中的同步标记就和Session中的同步标记作对比。在form首次提交的时候,这两个标记应该是一样的。如果标记不一样,那么该form就会禁止提交,一个错误就会返回给用户。在用户提交一个form时,如果按下浏览器中的后退按钮并尝试重新提交同一个form时,标记就会出现不匹配的现象。
另一方面,如果两个标记值匹配,那么我们就可以确信整个流程是正确的。在这种情况下,Session中的标记值就会被修改为一个新的值,同时允许提交该form。
还可以使用这个策略来控制对某些页面的直接访问,就好象上面资源保护中描述的一样。例如,假设一个用户将某个应用的页面A收藏到收藏夹中,而页面A 只允许通过页面B和C访问。当用户直接通过收藏夹来访问页面A,这时页面的访问顺序就是不正确的,这样同步标记将处在一个不同步的状态,或者它根本就不存在。不论怎样,访问都被禁止了。
------------------------------------------------------------------------
对于JSP入门的初级的学习者表单的提交是一个非常困扰的问题,那么如何避免Form表单多次提交呢?可以从以下的几个方面入手:
一.对于初学JSP可以先通过Javascript中设置
设置一个变量,只允许表单提交一次。
- ﹤scriptlanguage="javascript"﹥
- varcheckSubmitFlg=false;
- functioncheckSubmit(){
- if(checkSubmitFlg==true){
- returnfalse;
- }
- checkSubmitFlg=true;
- returntrue;
- }
- document.ondblclick=functiondocondblclick(){
- window.event.returnValue=false;
- }
- document.onclick=functiondoconclick(){
- if(checkSubmitFlg){
- window.event.returnValue=false;
- }
- }
- ﹤/script﹥
- ﹤html:formaction="myAction.do"method="post"onsubmit="returncheckSubmit();"﹥
二.对于JSP人门还要掌握Javascript的另一设置
将表单提交按钮或者image置为disable
- ﹤html:formaction="myAction.do"method="post"
- onsubmit="getElById('submitInput').disabled=true;returntrue;"﹥
- ﹤html:imagestyleId="submitInput"src="images/ok_b.gif"border="0"/﹥
- ﹤/html:form﹥
三.在JSP入门阶段也要注意善于利用STRUTS的同步令牌机制
利用同步令牌(Token)机制来解决Web应用中重复Form表单提交的问题,Struts也给出了一个参考实现。
基本原理:
服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
- if(isTokenValid(request,true)){
- //yourcodehere
- returnmapping.findForward("success");
- }else{
- saveToken(request);
- returnmapping.findForward("submitagain");
- }
STRUTS根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌的,具体实现可以参考TokenProcessor类中的generateToken()方法。
1. //验证事务控制令牌,﹤html:form ﹥会自动根据session中标识生成一个隐含input代表令牌,防止两次提交
2. 在action中:
- //﹤inputtype="hidden"name="org.apache.struts.taglib.html.TOKEN"
- //value="6aa35341f25184fd996c4c918255c3ae"﹥
- if(!isTokenValid(request))
- errors.add(ActionErrors.GLOBAL_ERROR,
- newActionError("error.transaction.token"));
- resetToken(request);//删除session中的令牌
3. action有这样的一个方法生成令牌
- protectedStringgenerateToken(HttpServletRequestrequest){
- HttpSessionsession=request.getSession();
- try{
- byteid[]=session.getId().getBytes();
- bytenow[]=
- newLong(System.currentTimeMillis()).toString().getBytes();
- MessageDigestmd=MessageDigest.getInstance("MD5");
- md.update(id);
- md.update(now);
- return(toHex(md.digest()));
- }catch(IllegalStateExceptione){
- return(null);
- }catch(NoSuchAlgorithmExceptione){
- return(null);
- }
- }
在更新的时候防止表单按钮重复点击,主要是用Session来做判断
四.在JSP入门时还要掌握页面方式
- ﹤inputtype="hidden"name="﹤%=com.lims.util.SynchroToken.TOKEN_NAME%﹥"value="﹤%=com.lims.util.SynchroToken.getToken(request)%﹥"﹥
- SynchroToken.java
- packagecom.lims.util;
- importorg.apache.struts.util.*;
- importjavax.servlet.http.*;
- importjavax.servlet.jsp.*;
- importorg.apache.struts.action.*;
- /**
- *﹤p﹥Title:SynchroToken﹤/p﹥
- *﹤p﹥Description:﹤/p﹥
- *﹤p﹥Copyright:Copyright(c)2004﹤/p﹥
- *﹤p﹥Company:NetStar﹤/p﹥
- *@authorJstar
- *@version1.0
- *Createdin2004/04/21
- */
- publicclassSynchroToken{
- publicfinalstaticjava.lang.StringTOKEN_NAME="_token";
- publicstaticbooleancheckToken(HttpServletRequestrequest){
- booleanisEqual=false;
- HttpSessionsession=request.getSession();
- StringformToken=request.getParameter(TOKEN_NAME);
- StringsessionToken=(String)session.getAttribute(TOKEN_NAME);
- System.out.println("formToken:"+formToken+"sessionToken:"+
- sessionToken);
- if(formToken!=null&&sessionToken==null){
- session.setAttribute(TOKEN_NAME,formToken);
- isEqual=true;
- }
- returnisEqual;
- }
- /**
- *Insertthemethod'sdescriptionhere.
- *Creationdate:(4/19/20043:23:25PM)
- *@returnjava.lang.String
- *@paramrequestjavax.servlet.http.HttpServletRequest
- */
- publicstaticStringgetToken(HttpServletRequestrequest){
- Stringtoken=""+System.currentTimeMillis();
- HttpSessionsession=request.getSession();
- if(session!=null){
- session.removeAttribute(TOKEN_NAME);
- }
- returntoken;z
- }
- /**
- *Insertthemethod'sdescriptionhere.
- *Creationdate:(4/19/20043:24:10PM)
- *@returnjava.lang.String
- */
- finalstaticjava.lang.StringgetTOKEN_NAME(){
- returnTOKEN_NAME;
- }
- publicstaticStringmessage(PageContextpageContext,Stringkey)throws
- JspException{
- returnRequestUtils.message(pageContext,null,null,key);
- }
- }
五.也可以通过添加中转页面的方式
对于JSP入门的角度讲,可以通过添加中转页面的方式,这样做虽然在视觉上不是很好,页面间显繁琐,但是这是有效地避免Form表单重复提交的好方式。