造成重复提交的两个原因:
在开发过程中,经常会遇到表单重复提交的问题,如用户注册,如果表单重复提交,那么一个用户就会注册多次,重复提交主要有两种原因。
1.服务器处理时间久,当用户在表单中填完信息,点击"提交"按钮后,由于服务器反应时间过长,没能及时看到响应信息,或处于其他目的,再次点击"提交"按钮,从而导致服务器到接收到多条相同的数据,后台在处理相同数据时可能会发生异常,以至于给用户带来不好的体验。
2.forward跳转引起的重复提交,在页面跳转的时候,有两种类型:请求转发和重定向,所谓请求转发是在服务器端进行跳转,对用户是透明的,此时浏览器的地址不会发生改变,重定向是在客户端发生跳转,跳转时浏览器中的地址会发生改变,如果我们在注册时,使用了请求转发,那么我们刷新页面时,就会引起表单的重复提交(因为地址栏中的地址没变,所以刷新就会重复提交)。
解决方案:
使用struts2的token拦截器或者tokenSession拦截器
token介绍:
1.当用户首次访问包含表单的页面时,服务器会在这次会话中创建一个Session对象,并产生一个令牌值,将这个令牌值作为隐藏输入域的值,随表单一起发送给服务器,同时将令牌值保存到Session中。
2.当用户提交页面时,服务器首先判断请求参数中的令牌值和Session中保存的令牌值是否相等,若相等,则清除Session中的令牌值,随后执行数据处理操作,如果不相等,则提示用户已经提交过表单,同时产生一个新的令牌值,保存到Session中,当用户重新访问提交数据页面时,将新产生的令牌值作为隐藏输入域的值。
案例:
index.jsp:
<body> <!-- 防止表单重复提交,记得在form表单里填上<s:token></s:token> --> <s:form action="userAction1.action" method="post"> <s:textfield name="uname"/> <s:token></s:token> <input type="submit" value="发送"/> </s:form> </body>
UserAction.java:
public class UserAction extends ActionSupport { private static final long serialVersionUID = 1L; /* 用户姓名 */ private String uname; public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String execute() { /* 输出姓名【如果token生效则不会输出用户姓名,而是会打印警告信息】 */ System.out.println("用户姓名:" + this.getUname()); return this.SUCCESS; } }
注:上述代码中的execute()方法,在表单重复提交的情况下是不会继续执行的而是会在控制台打印错误信息,如图:
struts.xml(struts2提供了token和tokenSession两个拦截器,任选一个即可):
<package name="demo" namespace="" extends="struts-default"> <action name="userAction1" method="execute" class="com.lixue.web.action.UserAction"> <!-- 默认拦截器栈 --> <interceptor-ref name="defaultStack"/> <!--strus2防止表单提交提供了两个拦截器token和tokenSession。tokenSession继承自token,通常情况下tokenSession客户端比较友好 --> <interceptor-ref name="token"/> <result name="success">/success.jsp</result> <!-- 当出现重复提交时,会跳转到invalid.token对应的视图 --> <result name="invalid.token">/error.jsp</result> </action> <!-- 这个是用tokenSession拦截(选择token和tokenSession中的任意一个即可) --> <action name="userAction2" method="execute" class="com.lixue.web.action.UserAction"> <!-- 默认拦截器栈 --> <interceptor-ref name="defaultStack"/> <!--strus2防止表单提交提供了两个拦截器token和tokenSession。tokenSession继承自token,通常情况下tokenSession客户端比较友好 --> <interceptor-ref name="tokenSession"/> <result name="success">/success.jsp</result> <!-- 当使用tokenSession出现重复提交时,并会跳转到invalid.token对应的视图,感觉比较友好 --> <result name="invalid.token">/error.jsp</result> </action> </package>
success.jsp:
<body> <s:property value="uname"/><br> <%=new Date()%> </body>
err.jsp:
<body> 您已经提交了表单,请不要重复提交。 </body>