说明

防止表单的重复提交在我们的日常生活中有很大的重要性。
例如,如果在我们使用支付宝支付时,有时由于网速太慢等缘故会导致用户多次点击支付按钮,如果未防止表单重复提交,那么将会产生严重的后果。
表单重复提交有两种情况:

1.提交表单后按下刷新按钮
2.多次点击提交按钮
那么如何防止表单的提交呢?
首先举个表单重复提交的小例子


小例子

jsp页面

一个输入框和一个提交按钮,提交后进入login.action动作

<body>
<s:form action="login">
<s:textfield name="username" label="用户名"/>
<s:submit label="提交"/>
</s:form>
</body>

success.jsp

仅仅一个提交成功提示。

<body>
提交成功!
</body>

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;
}

}

运行结果

struts2防止表单重复提交(s:token)_token使用


手速太慢 无法进行多次点击提交按钮。就点击后退按钮 进行重复提交。

在控制台我们看到输出了多次的表单提交。

基于此,我们应该如何解决呢?


解决方法

方法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后运行结果

struts2防止表单重复提交(s:token)_tokenSessi_02


通过结果我们可以发现

表单重复提交的第一种情况,页面刷新成功解决了。但是后退时再重新提交还是会表单重复提交。

遗留的问题:防不住后退,再提交。


方法2使用s:token生成令牌配合token拦截器

在struts2中有一个​​<s:token/>​​ 标签。该标签通过生成一个独一无二令牌,它会同时放在session和表单的隐藏域中。在进行表单提交时,token拦截器会进行令牌的验证,如果两次的令牌匹配,那么本次表单提交成功,否则提交失败。提交失败后会出现一个 invalid.token 无效的结果视图,配置相关的结果即可。有想了解token拦截器的请看在struts-default.xml(struts2的核心jar包里面有)文件中查看。

struts2防止表单重复提交(s:token)_token使用_03


使用​​<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;
}

}

运行结果:

struts2防止表单重复提交(s:token)_token使用_04

方法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>

运行结果:

struts2防止表单重复提交(s:token)_token使用_05


从结果中,虽然没有跳转到请勿重复提交页面,但是表单也没有重复提交。建议大家使用tokenSession这种方式。