在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。
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域中存储的标识号。
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
- 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
- 当前用户的Session中不存在Token(令牌)。
- 用户提交的表单数据中没有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