在Web开发中,对于处理表单重复提交是经常要面对的事情。那么,存在哪些场景会导致表单重复提交呢?表单重复提交会带来什么问题?有哪些方法可以避免表单重复提交?

表单重复提交的场景

1.场景一:服务端未能及时响应结果(网络延迟,并发排队等因素),导致前端页面没有及时刷新,用户有机会多次提交表单

2.场景二:提交表单成功之后用户再次点击刷新按钮导致表单重复提交

3.场景三:提交表单成功之后点击后退按钮回退到表单页面再次提交

表单重复提交的弊端

下面通过一个简单的示例进行说明。

  • 表单页面: test-form-submit-repeat.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>处理表单重复提交</title>
</head>
<body> <form action="<%=request.getContextPath()%>/formServlet.do" method="post">
姓名:<input type="text" name="username" />
<input type="submit" value="提交">
</form>
</body>
</html>
  • 后台Serlvet:FormServlet.java
public class FormServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String userName = req.getParameter("username"); try {
// 模拟服务端处理效率慢
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("插入数据:" + userName);
}
}

实验表单重复提交结果:

显然,从演示结果来看,如果出现表单重复提交,将会导致相同的数据被重复插入到数据库中。实际上,这是不应该发生的。

如何避免重复提交表单

关于解决表单重复提交,分为在前端拦截和服务端拦截2种方式。

1.在前端对表单重复提交进行拦截

在前端拦截表单重复提交可以通过多种方式实现:

(1)通过设置变量标志位进行拦截

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>处理表单重复提交</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/formServlet.do" method="post" onsubmit="return checkSubmit();">
姓名:<input type="text" name="username" />
<input type="submit" value="提交">
</form>
</body>
<script type="text/javascript">
// 设置表单提交标志
var submit = false;
function checkSubmit() {
if(!submit) {
// 表单提交后设置标志位
submit = true;
return true;
}
// 表单已经提交,不允许再次提交
console.log("请不要重复提交表单!");
return false;
}
</script>
</html>

(2)通过禁用按钮进行拦截

除了在前端通过设置标志位进行拦截之外,还可以在表单提交之后将按钮disabled掉,这样就彻底阻止了表单被重复提交的可能。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>处理表单重复提交</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/formServlet.do" method="post" onsubmit="return disabledSubmit();">
姓名:<input type="text" name="username" />
<input id="submitBtn" type="submit" value="提交">
</form>
</body>
<script type="text/javascript">
function disabledSubmit() {
// 在提交按钮第一次执行之后就disabled掉,避免重复提交
document.getElementById("submitBtn").disabled= "disabled";
return true;
}
</script>
</html>

当然,还可以直接在提一次提交之后将按钮隐藏掉。但是,是否需要这样做,需要考虑用户的操作体验是不是可以接受。

在前端拦截虽然可以解决场景一的表单重复提交问题,但是针对场景二(刷新)和场景三(后退重新提交)的表单重复提交是无能为力的。

2.在服务器端对表单重复提交进行拦截

在服务器端拦截表单重复提交的请求,实际上是通过在服务端保存一个token来实现的,而且这个在服务端保存的token需要通过前端传递,分三步走:

第一步:访问页面时在服务端保存一个随机token

public class FormServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
UUID uuid = UUID.randomUUID();
String token = uuid.toString().replaceAll("-", "");
// 访问页面时随机生成一个token保存在服务端session中
req.getSession().setAttribute("token", token);
req.getRequestDispatcher("/test-form-submit-repeat.jsp").forward(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

随机token的产生可以使用任何恰当的方式,在这里通过UUID产生。

第二步:将服务端端保存的随机token通过前端页面传递

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>处理表单重复提交</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/doFormServlet.do" method="post">
<!-- 隐藏域保存服务端token -->
<input type="hidden" name="token" value="<%=session.getAttribute("token")%>" />
姓名:<input type="text" name="username" />
<input id="submitBtn" type="submit" value="提交">
</form>
</body>
</html>

第三步:提交表单时在服务端通过检查token来判断是否为重复提交的表单请求

public class DoFormServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8"); if(checkRepeatSubmit(req)) {
System.out.println("请不要重复提交!");
return;
} // 在第一次处理表单之后需要清空token,这一步非常关键
req.getSession().removeAttribute("token"); String userName = req.getParameter("username");
try {
// 模拟服务端处理效率慢
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("插入数据:" + userName);
} // 检查表单是否为重复提交
private boolean checkRepeatSubmit(HttpServletRequest req) {
Object sessionTokenObj = req.getSession().getAttribute("token");
if(sessionTokenObj == null) {
// 表单重复提交
System.out.println("Session token is NULL!");
return true;
} String paramToken = req.getParameter("token");
if(paramToken == null) {
// 非法请求
System.out.println("Parameter token is NULL!");
return true;
} if(!paramToken.equals(sessionTokenObj.toString())) {
// 非法请求
System.out.println("Token not same");
return true;
}
return false;
}
}

显然,通过在服务端保存token的方式拦截场景二和场景三的表单重复提交是非常有效的。而且,这种方式同样可以拦截场景一的表单重复提交。

也就是说,对于拦截表单重复提交的终极解决方案是在服务器端进行拦截!不过,考虑到用户操作体验的问题,可能需要同时在前端进行拦截,这可以根据具体的产品设计而定。

另外,有意思的是:在最新的Firefox浏览版本(Firefox Quantum 59.0.1 64位)中,浏览器自己就能处理场景一的表单重复提交(但是不能处理场景二和场景三的表单重复提交)。



经过验证,在最新版的Chrome(Chrome 65.0.3325.181)浏览器中还不具备这个功能。

防止Web表单重复提交的方法总结的更多相关文章

  1. PHP使用token防止表单重复提交的方法

    本文实例讲述了PHP使用token防止表单重复提交的方法.分享给大家供大家参考,具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2 ...

  2. php 解决和避免form表单重复提交的方法

    在提交表单的时候,可能遇到网速等导致页面突然加载变慢,用户重复地点击提交按钮,将在数据库产生多条数据,导致不可控情况. 比如下面的情况就会导致表单重复提交: 点击提交按钮两次. 点击刷新按钮. 使用浏 ...

  3. php 解决表单重复提交实现方法介绍

    重复提交是我们开发中会常碰到的一个问题,除了我们使用js来防止表单的重复提交,同时还可以使用php来防止重复提交哦. 例1  代码如下 复制代码 <?php/* * php中如何防止表单的重复提 ...

  4. JS 防止表单重复提交的方法

    第一种:用flag标识,下面的代码设置checkSubmitFlg标志: <script language="”JavaScript”"> var checkSubmi ...

  5. java web解决表单重复提交问题

    我们大家再进行web开发的时候,必不可少会遇见表单重复提交问题.今天就来给总结如何解决表单提交问题,欢迎大家交流指正. 首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提 ...

  6. java web解决表单重复提交

    首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提交?2.什么情况下会出现表单重复提交?3.什么情况需要避免表单重复提交? 什么叫表单提交问题,说白了,就是同一份信息,重 ...

  7. PHP防止表单重复提交的解决方法

    PHP+SESSION防止表单重复提交 index.php 当前表单页面is_submit设为0 SESSION_START(); $_SESSION['is_submit'] = 0; <fo ...

  8. php防止表单重复提交

    解决方案一:引入cookie机制来解决 提交页面代码如下a.php代码如下: <form id="form1" name="form1" method=& ...

  9. php通过token验证表单重复提交

    PHP防止重复提交表单 2016-11-08 轻松学PHP 我们提交表单的时候,不能忽视的一个限制是防止用户重复提交表单,因为有可能用户连续点击了提交按钮或者是攻击者恶意提交数据,那么我们在提交数据后 ...

随机推荐

  1. 19.java反射入门

    一.反射机制是什么反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...

  2. Spring Security Oauth2 的配置

    使用oauth2保护你的应用,可以分为简易的分为三个步骤 配置资源服务器 配置认证服务器 配置spring security 前两点是oauth2的主体内容,但前面我已经描述过了,spring sec ...

  3. 最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...

  4. 微信小程序开发 (资料汇总,谁还没被坑过?希望助你绕过一些坑)

    最近帮人家做一个微信小程序,刚好想熟悉一下.由于牵扯到多用户使用系统,以及数据共享,所以自然架构选择了,客户端和服务器的方式. 后台服务器是windows server,后台程序是.Net  WebA ...

  5. virtualbox 设置centos7 双网卡上网

    上次用virtualbox安装centos6.6,这次装了一个centos7.0.用两个版本的配置还是大同小异的. 1.修改/etc/sysconfig/network-scripts/ifcfg-e ...

  6. webpack4

    本地安装: npm init -y cnpm install webpack webpack-cli webpack-dev-server --save-dev 然后装一些所需要的loader和插件: ...

  7. Django中间件2

    前戏 我们在前面的课程中已经学会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面.我们通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可能也需要加上装饰 ...

  8. 【转帖】系统软件工程师必备技能-进程内存的working set size(WSS)测量

    系统软件工程师必备技能-进程内存的working set size(WSS)测量 2018年12月28日 18:43:01 Linuxer_ 阅读数:145 https://blog.csdn.net ...

  9. NOIP2001提高组复赛B 数的划分

    题目链接:https://ac.nowcoder.com/acm/contest/249/B 题目大意: 略 分析1(记忆化搜索): 方法为减而治之,把n划分成k份的答案就相当于每次把n分成a,b两个 ...

  10. springboot 出现 Connection refused: connect

    总结网上的方法再结合我自己的方法 1. springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectEx ...