说明
防止表单的重复提交在我们的日常生活中有很大的重要性。
例如,如果在我们使用支付宝支付时,有时由于网速太慢等缘故会导致用户多次点击支付按钮,如果未防止表单重复提交,那么将会产生严重的后果。
表单重复提交有两种情况:
1.提交表单后按下刷新按钮
2.多次点击提交按钮
那么如何防止表单的提交呢?
首先举个表单重复提交的小例子
小例子
jsp页面
一个输入框和一个提交按钮,提交后进入login.action动作
<body>
<s:form action="login">
<s:textfield name="username" label="用户名"/>
<s:submit label="提交"/>
</s:form>
</body>
success.jsp
仅仅一个提交成功提示。
struts.xml
动作类成功执行后通过转发的方式跳转到success.jsp页面。
<action name="login" class="com.scx.web.action.UserAction" method="login">
<result/success.jsp</result>
</action>
UserAction类
每次用户提交表单后,输出表单提交的提示。
public class UserAction extends ActionSupport
private String username;
public String login(){
System.out.println(username+"表单提交啦");
return SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
运行结果
手速太慢 无法进行多次点击提交按钮。就点击后退按钮 进行重复提交。
在控制台我们看到输出了多次的表单提交。
基于此,我们应该如何解决呢?
解决方法
方法1:使用重定向
<action name="login" class="com.scx.web.action.UserAction" method="login">
<result/success.jsp</result>
</action>
在用户表单提交后我们使用的是转发的方式跳转到success.jsp页面。我们可以通过重定向的方式来解决页面重复刷新的问题。
通过设置type=”redirect”来实现重定向。
<action name="login" class="com.scx.web.action.UserAction" method="login">
<!--使用重定向的方式 -->
<result type="redirect">/success.jsp</result>
</action>
使用方法1后运行结果
通过结果我们可以发现
表单重复提交的第一种情况,页面刷新成功解决了。但是后退时再重新提交还是会表单重复提交。
遗留的问题:防不住后退,再提交。
方法2使用s:token生成令牌配合token拦截器
在struts2中有一个<s:token/>
标签。该标签通过生成一个独一无二令牌,它会同时放在session和表单的隐藏域中。在进行表单提交时,token拦截器会进行令牌的验证,如果两次的令牌匹配,那么本次表单提交成功,否则提交失败。提交失败后会出现一个 invalid.token 无效的结果视图,配置相关的结果即可。有想了解token拦截器的请看在struts-default.xml(struts2的核心jar包里面有)文件中查看。
使用<s:token/>
标签的要求是,把此标签放到form表单中,并且在动作类中生成相应的get,set方法。
jsp页面
在form中添加<s:token/>
标签
<body>
<s:form action="login">
<s:token/>
<s:textfield name="username" label="用户名"/>
<s:submit label="提交"/>
</s:form>
</body>
struts.xml
添加token拦截器,由于token拦截器不再默认的拦截器栈中,使用token拦截器后默认的拦截器栈不起作用,所以还需要添加默认的拦截器栈。
name="login" class="com.scx.web.action.UserAction" method="login">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="token"/>
<result >/success.jsp</result>
<result name="invalid.token">/message.jsp</result>
</action>
UserAction动作类
添加token变量 生成相应的get,set方法。
public class UserAction extends ActionSupport
private String username;
private String token;
public String login(){
System.out.println(username+"表单提交啦");
return SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
运行结果:
方法3使用s:token生成令牌配合tokensession拦截器
tokensession拦截器同样也能够首先token拦截器的效果,只不过tokensession在接到非法令牌时将提交的数据保存在session中;只会处理第一次请求,当重复提交时,不会再处理。
配置和token一样。
只是把struts.xml中的token拦截器换成tokenSession拦截器
name="login" class="com.scx.web.action.UserAction" method="login">
<interceptor-ref name="tokenSession"/>
<interceptor-ref name="defaultStack"/>
<result >/success.jsp</result>
<result name="invalid.token">/message.jsp</result>
</action>
运行结果:
从结果中,虽然没有跳转到请勿重复提交页面,但是表单也没有重复提交。建议大家使用tokenSession这种方式。