在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. python进阶之time模块详解

    Time模块 Time模块包含的函数 Time模块包含了一下内置的函数,既有时间处理的,也有转换时间格式的: 序号 函数及描述 1 time.altzone 返回格林威治西部的夏令时地区的偏移秒数.如 ...

  2. 我的第一个python web开发框架(29)——定制ORM(五)

    接下来我们要封装的是修改记录模块. 先上产品信息编辑接口代码 @put('/api/product/<id:int>/') def callback(id): ""&q ...

  3. CSAPP:第八章 异常控制流2

    CSAPP:第八章 异常控制流2 关键点:进程控制.信号 8.4 进程控制8.5 信号 8.4 进程控制   Unix提供了大量从C程序中操作进程的系统调用.8.4.1 获取进程ID  每个进程都有一 ...

  4. 21 python 初学(json pickle shelve)

    json: # _author: lily # _date: 2019/1/19 import json my_dict = {'name': 'lily', 'age': 18} f = open( ...

  5. matlab读取cvs文件的几种方法

    matlab读取CVS文件的几种方法: 1,实用csvread()函数   csvread()函数有三种使用方法: 1.M = csvread('filename')2.M = csvread('fi ...

  6. android H5支付 网络环境未能通过安全验证,请稍后再试

    android做混合开发微信H5支付时碰到的一个问题. 解决办法:把所使用的WebView中重新如下方法即可 webView.setWebViewClient(new WebViewClient() ...

  7. Word Representations 词向量

    常用的词向量方法word2vec. 一.Word2vec 1.参考资料: 1.1) 总览 https://zhuanlan.zhihu.com/p/26306795 1.2) 基础篇:  深度学习wo ...

  8. LODOP、C-Lodop简短排查语句

    https使用,故障:1.是https网站吗,https扩展版C-Lodop如何使用 参考http://www.c-lodop.com/faq/pp32.html2.双击桌面上的c-lodop快捷方式 ...

  9. vue 自定义指令的使用案例

    参考资料: 1. vue 自定义指令: 2. vue 自定义指令实现 v-loading: v-loading,是 element-ui 组件库中的一个用于数据加载过程中的过渡动画指令,项目中也很少需 ...

  10. Amazon SNS (Simple Notification Service) Using C# and Visual Studio

    SNS (Amazon Simple Notification Services) Amazon SNS (Amazon Simple Notification Services) is a noti ...