java jsp处理用户多次点击提交按钮造成数据重复,最常见的方法是点击按钮提交之后,让线程休眠几秒再执行,如下代码:
成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站设计制作、成都做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的易门网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
1.提交的表单页面
%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%
!DOCTYPE HTML
html
head
titleForm表单/title
/head
body
form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post"
用户名:input type="text" name="username"
input type="submit" value="提交" id="submit"
/form
/body
/html
2.处理提交表单的servlet,里面有提供防止重复提交的代码,如下:
package xdp.gacl.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DoFormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//客户端是以UTF-8编码传输数据到服务器端的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
request.setCharacterEncoding("UTF-8");
String userName = request.getParameter("username");
try {
//让当前的线程睡眠3秒钟,模拟网络延迟而导致表单重复提交的现象
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("向数据库中插入数据:"+userName);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
在后续打包运行的过程之中发现了一个问题。就是使用jar包启动时,监听到的进程名称是jar包的名字,而非启动类的类名。比如我打出的包名为Test.jar,使用该包启动后,进程对应的名称也是Test.jar。
1.js方法解决:关于js方法解决就是说通过js动态控制提交按钮不能多次点击,或者多次点击不起作用。
方案一:通过设立标识使表单不能重复提交:
var flag=true; function Sub(){ if(flag){
flag = false;
document.form1.onsubmit();
}
}
方案二:一次点击后使得提交按钮变成不可用
input type="button" value="login" onclick="this.disabled=true;this.form.submit();" /
总的来说,js解决方案是基本可以防止重复点击提交按钮造成的重复提交问题,但是前进后退操作,或者F5刷新页面等问题并不能得到解决。
最重要的一点,前端的代码只能防止不懂js的用户,如果碰到懂得js的编程人员,那js方法就没用了。
2.设置HTTP报头,控制表单缓存,使得所控制的表单不缓存信息,这样用户就无法通过重复点击按钮去重复提交表单。
meta http-equiv="Cache-Control" content="no-cache, must-revalidate"
但是这样做也有局限性,用户在提交页面点击刷新也会造成表单的重复提交。
3.通过PRG设计模式(用来防止F5刷新重复提交表单):
PRG模式通过响应页面Header返回HTTP状态码进行页面跳转替代响应页面跳转过程。具体过程如下:
客户端用POST方法请求服务器端数据变更,服务器对客户端发来的请求进行处理重定向到另一个结果页面上,客户端所有对页面的显示请求都用get方法告知服务器端,这样做,后退再前进或刷新的行为都发出的是get请求,不会对server产生任何数据更改的影响。
但此方法也不能防止所有情况:例如用户多次点击提交按钮;恶意用户避开客户端预防多次提交手段,进行重复提交请求;
以上都说的是在客户端如何防止表单重复提交,下面说一下服务器端有哪些可行的方法。
4.如果是注册或存入数据库的操作,可以通过在数据库中字段设立唯一标识来解决,这样在进行数据库插入操作时,因为每次插入的数据都相同,数据库会拒绝写入。这样也避免了向数据库中写入垃圾数据的情况,同时也解决了表单重复提交问题。
但是这种方法在业务逻辑上感觉是说不过去的,本来该有的逻辑,缺因为数据库该有的设计隐藏了。而且这种方法也有一定的功能局限性,只适用于某系特定的插入操作。
5.session方法:
在struts框架中防止表单重复提交的方法是生成Token存入session,以此判断表单是否是第一次提交。以下给大家解释一下运行流程。
首先客户端请求服务器中的表单,服务器将客户机所请求的表单发给客户机同时发送一个特殊的随机数(Token)作为表单号存在表单的隐藏域中(type=hidden),并且存入服务器端的session中。
在客户端填写完表单内容向服务器提交时,同时也将隐藏域中的表单号发给服务器端,服务器端此时会检测服务器端的表单号是否存在,如果存在,则进行提交操作,并删除此表单号,否则,服务器视为客户机端重复提交表单,不予操作。
此处贴出生成Token的代码(保证随机数的独一无二性):
class Token{ private Token(){} private static Token instance = new Token();
public Token getinstance(){ return instance;
}
//随机数发生器
public String getToken(){
String token = System.currentTimeMillis() + "" + new Random().nextInt();//获得毫秒数加随机数
try {
MessageDigest md = MessageDigest.getInstance("md5"); byte[] md5 = md.digest(token.getBytes());
BASE64Encoder base = new BASE64Encoder();
base.encode(md5);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
要强调的是,利用session方法解决表单重复问题是十分完美的,基本上可以应对各种重复提交问题。
但!是不是之前在客户端防止表单重复提交的种种方法就不使用了呢?
答案是否定的,我们需要多种方法混合使用才能达到最好的效果,也许有人会问,不是说session方法基本可以应对各种重复提交问题了吗?
这里我们所说的达到最好效果指的是,给用户更好地体验,例如用户点击了提交按钮,这时将按钮变为不可用的,用以告诉用户你已经提交内容了,不可重复提交。还有如果无论什么情况都用session防止表单重复提交问题,反而无形的增加了服务器端的负担。
可以用一个Set类型的容器如:HashSet,来存每个文件的特征值:如路径或文件名。每遍历到一个新文件,先判断Set中是否已存在这个元素,是的话就continue,否的话就将其添加到Set中。
因为Set类的容器都是不允许有重复元素的