基于Struts的Web系统表单重复提交问题的解决方案*
2010-03-21孔祥盛
曹 琨,孔祥盛
(新乡学院计算机与信息工程学院,河南新乡453003)
浏览器用户在使用基于Struts的Web应用系统时,由于网络延迟使浏览器用户认为Web应用系统响应失败,导致用户重复提交表单数据。由于表单数据的重复提交,导致网络和服务器资源的浪费,并有可能导致数据库数据冗余,严重时可导致服务器死机现象,如何避免表单数据重复提交显得格外重要[1]。
1 Struts系统中表单重复提交问题的解决方案
基于Struts的Web应用系统避免表单重复提交有以下四种解决方案:
①使用Javascript解决表单重复提交问题;②使用Cookie解决表单重复提交问题;③使用Session解决表单重复提交问题;④使用Struts同步令牌Token解决表单重复提交问题。
第①种方案为浏览器端的解决方案,该方案可以防止用户重复点击提交按钮产生的表单重复提交问题,该方案受浏览器端制约;第②、③、④种方案为服务器端的解决方案,这三种方案可以防止用户刷新页面产生的表单重复提交问题[2]。
1.1 使用Javascript解决表单重复提交问题
通过使用提交按钮的onClicks事件检测用户的提交状态可以解决表单重复提交问题[3]。实现方法是:当用户点击提交按钮后,使用按钮的onClick事件控制按钮,将按钮变为灰色失效状态(图1),用户不能再次点击提交按钮,从而解决用户重复点击提交按钮时造成的表单重复提交问题,实现代码如下:
<form method="post"name="thisForm"action="test.do" >
<input name="text"type="text"/>
<input name="submitButton"value="提交"type="submit"
onClick="document.thisForm.submitButton.
value= '正在提交,请等待...';
document.thisForm.submitButton.disabled=true;document.thisForm.submit();"/>
</form>
图1 使用onClick事件解决表单重复提交问题
另外还可以使用表单的onSubmit事件检测用户的提交状态解决表单重复提交问题[3]。实现方法是:使用Javascript记录表单的提交次数(默认为1),当表单提交次数大于1时,将弹出图2所示的对话框,提示用户已经提交表单,从而解决用户重复点击提交按钮造成的表单重复提交问题,实现代码如下:
<script language="javascript">
<!--
var submitcount=1;
function submitOnce(form){
if(submitcount==1){
submitcount++;
return true;
}else{
alert("正在操作,请不要重复提交表单数据!");
return false;
}
}
//-->
</script>
<form method="post"name="thisForm"action="test.do"onSubmit="return submitOnce(this)" >
<input name="text"type="text"/>
<input name="submitButton"value="提交"type="submit"/>
</form>
图2 使用onSubmit事件解决表单重复提交问题
由于Javascript脚本程序运行在浏览器端,使用该方法可以防止用户重复点击提交按钮导致的表单重复提交问题,但是由于用户可以屏蔽浏览器Javascript脚本程序的运行,因此该方法仅适用于浏览器开启了Javascript功能。当浏览器屏蔽了Javascript脚本程序运行时,可以选择下面三种服务器端的解决方案。
1.2 使用Cookie解决表单重复提交问题
在Struts的Action中使用Cookie记录表单提交的状态可以解决表单重复提交问题[3],实现方法是:当用户第一次点击提交按钮后触发Struts中的Action代码运行,该Action为浏览器用户设置一个Cookie,然后运行业务逻辑代码,最后取消该Cookie。在运行业务逻辑代码的过程中,当用户刷新页面时,判断Cookie是否已经存在,若存在,Action将页面重定向到失败页面,从而解决用户刷新页面时造成的表单重复提交问题,实现代码如下:
Cookie cookies[]=request.getCookies();
for(int i=0;i< cookies.length;i++){
Cookie cookie=cookies[i];
if("submitOnce".equals(cookie.getName())){
return mapping.findForward("failure");
}
}
Cookie cookie=new Cookie("submitOnce","submitOnce");
cookie.setMaxAge(60);
response.addCookie(cookie);
//业务逻辑代码
cookie.setMaxAge(0);
由于Cookie信息保存在浏览器端,用户可以阻止浏览器使用Cookie,因此该方法仅适用于浏览器开启Cookie。当浏览器禁用Cookie时,可以选择下面两种服务器端的解决方案。
1.3 使用Session解决表单重复提交问题
在Struts的Action中使用Session记录表单提交的状态可以解决表单重复提交问题[3],实现步骤如下:
1)包含有form表单的页面通过应用服务器程序动态生成,服务器程序为每次产生的form表单都分配一个唯一随机标识号random,并在Session和form表单的一个隐藏域中进行保存。
2)当用户提交form表单时,负责接收这一请求的Struts的Action程序比较form表单隐藏域中的标识号与存储在Session中的标识号是否相同,当遇到下列情形时,Action程序将认为表单重复提交,Action程序将页面重定向到失败页面:
当前用户Session不存在表单标识;用户提交的表单数据没有标识号;存储在当前用户的Session中的标识号与表单数据中的标识号不同。
实现代码如下:
1)产生random并把random放入session对象和隐藏域中。
<%
int random=(int)(10000*Math.random());
session.setAttribute("random",random);
%>
<form method="post"name="thisForm"action="test.jsp" >
<input name="text"type="text"/>
<input type="hidden"name="random"value="<%=random%>">
<input name="submitButton"value="提交"type="submit"/>
</form>
2)判断是否重复提交的Struts的Action的代码实现。
Session session=request.getSession();
if(request.getParameter("random")==null||session.getAttribute("random")==null){return mapping.findForward("failure");
}else{
int randomSession = ((Integer)session.getAttribute("random")).intValue();
int randomRequest=Integer.parseInt(request.get-Parameter("random"));
if(randomSession!=randomRequest){
return mapping.findForward("failure");
}else{
//业务逻辑代码
}
}
由于Session信息保存在服务器端,浏览器用户无法干预Session的使用,因此该方法适用于任何场景。
1.4 通过Struts同步令牌Token解决表单重复提交问题
Struts的Action提供了另一个防止表单重复提交的方法,即同步令牌(Token)机制[4]。在 Struts中,通过TokenProcessor类来创建和处理令牌,Struts根据用户会话ID和当前系统时间生成一个唯一的同步令牌(Token)。同步令牌机制的基本原理是[5]:应用服务器在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话Session中的令牌值进行比较,看是否匹配。在处理完该请求后,且在响应发送给浏览器端之前,会产生一个新的令牌,该令牌除传给浏览器端以外,也会将用户会话Session中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,浏览器端传过来的令牌就和服务器端的令牌不一致,从而有效地避免表单重复提交问题的发生。
在Struts中使用同步令牌(Token)解决表单重复提交问题的步骤如下:
1)在转发jsp页面之前,创建一个新的同步令牌,方法如下:
saveToken(request);
在转发 jsp页面时,<htm l:form>会自动根据session中标识ID生成一个表单隐藏域同步令牌数据,防止重复提交表单,生成的表单隐藏域格式如下:
<input type=”hidden”name=”org.apache.struts.taglib.html.TOKEN”
value=”6aa35341f25184fd996c4c918255c3ae”>,其中的value是调用TokenProcessor类中的generateToken()方法获得的。
2)在jsp页面的表单提交处理Action中加入同步令牌判断方法,代码如下:
if(isTokenValid(request,true)){
//业务逻辑代码
}else{
//表单重复提交
saveToken(request);
return mapping.findForward(“failure”);
}
resetToken(request);//删除session中的令牌
由于使用同步令牌解决表单重复提交问题的原理是基于Session会话的原理,因此该方法适用于所有Struts系统。
2 结语
本文介绍了基于Struts的Web系统中表单重复提交的四种解决方案,并详细分析了每种方案的设计方法及应用场景,目前这四种解决方案已经运用到很多基于Struts的Web系统中。
[1]朱国辉,周琪云,朱文生.Web应用中重复提交的探讨[J].计算机与现代化,2006,(7):75 -77.
[2]阮景奎,宋国柱.重复表单提交实现[J].湖北汽车工业学院学报,2007,(9):33 -35.
[3]Elliot White III,Jonathan Eisenhamer.PHP 5 in Practice[M].Sams,2006.
[4]廖仪奎.Java Web开发之Struts编程基础与实例精讲[M],北京:中国电力出版社,2006.
[5]杨兵.令牌技术在Web项目中的研究与应用[J].计算机与现代化,2005,(7):110 -112.