在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

 

1.什么是表单重复提交

 

> 在不刷新表单页面的前提下:
        >> 多次点击提交按钮
        >> 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".
        >> 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)"

 

> 注意:
        >> 若刷新表单页面, 再提交表单不算重复提交
        >> 若使用的是 redirect 的响应类型(地址栏发生变化), 已经提交成功后, 再点击 "刷新", 不是表单的重复提交

 

2、客户端利用JavaScript防止表单重复提交

既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:

修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交

 

 

 

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>首页</title>
<script type="text/javascript">
/************客户端JS防止重复提交表单方法一
 
 var isSubmit=false;
function dosubmit(){
if(!isSubmit){    isSubmit=true;
                  return true;}
else{return false;}
    
}
************/
//第二中方法设置表单的提交按钮点击一次后不能点击,document后面没括号,刷新跟后退同样可以重复提交。
function dosubmit(){
    
    document.getElementById("ss").disabled='disabled';
    alert("HH");
    return true;
}
</script>
</head>
<body>
<form action="/Servlet/lOGIN1"  method="post" onsubmit="return dosubmit()">
用户名 :<input type="text" name="name"><br/><br/>
<input id="ss" type="submit" value="提交" >
</form>
</body>
</html>

 

我们看看使用了JavaScript来防止表单提交重复是否可以成功,运行效果如下:

 

后退之后按钮不能点击

另外还有一种做法就是提交表单后,将提交按钮隐藏起来,这种做法和将提交按钮设置为不可用是差不多的,个人觉得将提交按钮隐藏影响到页面布局的美观,并且 可能会让用户误以为是bug(怎么我一点击按钮,按钮就不见了呢?用户可能会有这样的疑问),我个人在开发中用得比较多的是表单提交后,将提交按钮设置为 不可用,反正使用JavaScript防止表单重复提交的做法都是差不多的,目的都是让表单只能提交一次,这样就可以做到表单不重复提交了

三、利用Session防止表单重复提交(常用)

在服务器端解决,在服务器端解决就需要用到session了。

具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为 Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏 域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的 Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的 Session域中存储的标识号。
  在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

    1. 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
    2. 当前用户的Session中不存在Token(令牌)
    3. 用户提交的表单数据中没有Token(令牌)

 

1.产生随机数(令牌)跳转到表单页面的Java

package Session;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import sun.misc.BASE64Encoder;

@WebServlet("/ServletForm_2")
public class ServletForm_2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    public ServletForm_2() {
        super();
        
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        TokenProcessor tp=TokenProcessor.getTp();
        String rand=tp.getToken();
        HttpSession session=request.getSession();
        session.setAttribute("rand", rand);   //用session将数据带过去
        request.getRequestDispatcher("/SecondForm.jsp").forward(request,response);;
                
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        doGet(request, response);
    }

}


//令牌处理器,产生一个随机数,单利模式,一个对象产生
class TokenProcessor{
    //单利模式
    /***
     * 1.构造方法私有
     * 2.创建一个对象
       3.公开一份方法暴露对象
     */
    private  TokenProcessor() {
        
    }

        private static TokenProcessor tp=new TokenProcessor();

    public static TokenProcessor getTp(){
        return tp;
    }
    
    public String getToken(){
        //token是系统当前时间毫秒数+随机数变为的字符串。长度不同
        String token=System.currentTimeMillis()+new Random().nextInt()+"";
        //利用MD5摘要算法得到固定长度的字符串
        try {
            MessageDigest md=MessageDigest.getInstance("md5");
            //根据MD5算法得到数据的指纹
            byte[] md5=md.digest(token.getBytes());
            //BASE64编码,3BYTE变为4byte(每6位前面加2零)
            BASE64Encoder encoder=new BASE64Encoder();
            String ss=encoder.encode(md5);
            return ss;
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e); 
        }
    }
    
    
}

 

表单JSP:

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>

<form action="/Servlet/lOGIN2" method="post">
用户名:<input type="text"><br/><br/>
密码:<input type="password"><br/>
<input type="hidden"  name="hid" value="<%= request.getSession().getAttribute("rand") %>">
<input type="submit" value="提交">



</form>



</body>
</html>

 

处理表单的Servlet

package Session;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class lOGIN2
 */
@WebServlet("/lOGIN2")
public class lOGIN2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public lOGIN2() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //判断带过的随机数是否有效
        boolean isValied=isTokenValid(request);
        
        if(!isValied){System.out.println("无效,请不要重复提交!"); return ;}
        
        
        request.getSession().removeAttribute("rand");
        System.out.println("正在提交。。。。。。。。。");
        
        
        
    }

    private boolean isTokenValid(HttpServletRequest request) {
    

    //服务器端带随机数
    String ser_hid=(String) request.getSession().getAttribute("rand");
    
    //客户端带过来带随机数
    String cli_hid=request.getParameter("hid");
    System.out.println(ser_hid+"         "+cli_hid+"    5");
        if(cli_hid==null){return false;}
        if(ser_hid==null){return false;}
        if(!cli_hid.equals(ser_hid)){return false;}
        return true;
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

 

结果:  http://localhost:8080/Servlet/ServletForm_2

查看页面源码:

 

第一次提交:

 

刷新页面:

 

后退后提交:

 

 

 

********: struts的防止表单重复提交比较简单,参考:  http://www.cnblogs.com/qlqwjy/p/7190272.html

 

【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】