您的位置:首页 > 编程语言 > Java开发

Struts2防止表单重复提交

2013-06-13 01:45 337 查看

造成表单重复提交的原因:

服务器处理时间久。当用户在表单中填完信息,点击“提交”按钮后,由于服务器反应时间过长没能及时看到响应信息,或者出于其它目的,再次点击“提交”按钮,从而导致在服务器端接收到两条或多条相同的信息。如果信息需要存储到后台数据库中,如此以来就会产生数据库操作异常提示信息,以至于给用户带来错误信息提示,从而给用户的使用带来不便。
forward跳转引起的重复提交。当用户将信息提交到服务器,服务器响应采用forward方式调转到下一个页面后,此时地址栏中显示的是上个页面的URL,若刷新当前页面,浏览器会将再次提交用户先前输入的数据,就会再次出现表单重复提交的问题。当然你可以选择redirect方式跳转页面,这样就不会出现重复提交的问题;但有时为了达到某种效果或式。者出于网站安全的目的需要隐藏网页跳转,而不得不采用forward跳转方

解决原理以及方案:


token原理

 当用户首次访问包含表单的页面时,服务器会在这次会话中创建一个session对象,并产生一个令牌值,然后将这个令牌值作为隐藏输入域的值,随表单一起发送到服务器端,同时将令牌值保存到Session中。
 当用户提交页面时,服务器首先判断请求参数中的令牌值和Session中保存的令牌值是否相等,若相等,则清楚Session中的令牌值,然后执行数据处理操作。如果不相等,则提示用户已经提交过了表单,同时产生一个新的令牌值,保存到Session中。当用户重新访问提交数据页面时,将新产生的令牌值作为隐藏输入域的值。




解决方案

1.使用客户端脚本
提到客户端脚本,经常使用的是JavaScript进行常规输入验证。在下面的例子中,我们使用它处理表单的重复提交问题,请看下面的代码:

   

 


<form method="post" name="register" action="test.php" enctype=


"multipart/form-data"


"multipart/form-data"> 


<input name="text" type="text" id="text" />


<input name="cont" value="提交" type="button" onClick="document.


register.cont.value='正在提交,请等待

';document.register.cont.


disabled=true;document.the_form.submit();"


register.cont.value='正在提交,请等待

';document.register.cont.


disabled=true;document.the_form.submit();"> 


</form>



 
上述方法,主要是将按钮的提交状态改变为disabled。
 
2.利用Struts2的令牌机制。
防止表单重复提交主要用的到标签是<s: token />,拦截器 <interceptor-ref name="token" />,还有一个默认的返回值<result name="invalid.token">/input.jsp</result>
在页面加载时,<s: token />产生一个GUID(Globally Unique Identifier,全局唯一标识符)值的隐藏输入框如:
<input type="hidden" name="struts.token.name" value="struts.token"/>
<input type="hidden" name="struts.token" value="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR"/>
同时,将GUID放到会话(session)中;在执行action之前,“token”拦截器将会话token与请求token比较,如果两者相同,则将会话中的token删除并往下执行,否则向actionErrors加入错误信息。如此一来,如果用户通过某种手段提交了两次相同的请求,两个token就会不同。
下面用零配置来演示 token的作用

/WEB-INF/content/test-success.jsp


<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>


<%@ taglib prefix="s" uri="/struts-tags" %>


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<html>


<head>


</head>






    <body>


       <s:actionerror/>


       <s:form action="test!save.action" method="POST">


           <s:textfield name="message" label="请输入您的信息"/>


           <s:token name="token"/>


           <s:submit value="确定" />


       </s:form>


    </body>


</html>





/WEB-INF/content/error.jsp


<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>


<%@ taglib prefix="s" uri="/struts-tags" %>


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<html>


<body>


<s:actionerror/>


    不能重复提交表单!


</body>


</html>





 

/WEB-INF/content/test-ok.jsp

 


<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>


<%@ taglib prefix="s" uri="/struts-tags" %>


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<html>


    <body>


       SAVE OK!


    </body>


</html>





TestAction.java


package com.fun.actions;






import org.apache.struts2.convention.annotation.InterceptorRef;


import org.apache.struts2.convention.annotation.Result;


import org.apache.struts2.convention.annotation.Results;






import com.opensymphony.xwork2.ActionSupport;


@Results({


@Result(name="invalid.token",location="/index.html",type="redirect")}


@Result(name="invalid.token",location="/index.html",type="redirect")})


@InterceptorRefs({@InterceptorRef(value="token",params={"includeMethods","save"}),@InterceptorRef("defaultStack") })


public class TestAction extends ActionSupport {


    private String message;


    public String execute(){


    return SUCCESS;


    }


    








    public String save(){


    return "ok";


    }


    public String getMessage() {


       return message;


    }


    public void setMessage(String message) {


       this.message = message;


    }


    


}


    private String message;


    public String execute(){


    return SUCCESS;


    }


    return SUCCESS;


    }


    








    public String save(){


    return "ok";


    }


    return "ok";


    }


    public String getMessage() {


       return message;


    }


       return message;


    }


    public void setMessage(String message) {


       this.message = message;


    }


       this.message = message;


    }


    


}





其实,最主要的就是 token拦截器中还有 includeMethods()这个方法,表示的是,Action中的哪个方法需要经过拦截器。属性excludeMethods()这个方法表示的是哪个方法不经过拦截器。如果,没有加上“defaultStack”这个拦截器的话,则ActionContext的值将为null。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: