视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
防止重复提交 仅提交一次的终极绝杀技
2020-11-27 22:42:56 责编:小采
文档

作者:Silent Void
出处:http://happyhippy.cnblogs.com/
最近一个客户,老抱怨每个月总有几条重复的业务数据;但创建该业务数据的页面,我们已经应用了常规的防重复提交技术,为啥还这样呢……
1. 常规防重复提交
代码如下:<asp:Button runat="server" ID="btnPostBack1" Text="按我1" UseSubmitBehavior="false" OnClientClick="this.disabled=true;this.form.submit();"/>

这里需要注意的是:
(1). UseSubmitBehavior="false" :需要设置成false,这样生成了个的input的类型才是button;如果用默认的true,生成的input的类型为submit,postback到服务器端,将不会触发button的服务器端事件;
(2). Button不能有ValidationGroup属性,否则,postback到服务器端,将不会触发button的服务器端事件;

2. 当遇上Validator控件
如果页面上使用了Validator控件,继续使用上面1种的方法,我们会发现,Validator控件失效了。因为将button disable掉后,提交的时候将不再使用Validator控件进行校验。
针对此问题,一个解决方案,就是提交前手动校验一次。如果我们用IE Develop tool来跟踪提交的JS代码,我们会发现asp.net是在Page_ClientValidate函数中来完成Validator中设置的校验,所以下面手工调用一次该方法,如果校验失败,则不提交:
代码如下:


1: <script type="text/javascript">
function disableButton(button)
{
if (typeof (Page_ClientValidate) == 'function' && Page_ClientValidate() == false)
{
return false;
}
button.disabled = true;
return true;
}
</script>

<div>
<asp:TextBox runat="server" ID="tbxInput1"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="tbxInput1"
ValidationGroup="Group1" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:Button runat="server" ID="btnPostBack1" Text="按我1" UseSubmitBehavior="false"
OnClientClick="disableButton(this);" OnClick="btnPostBack_Click">
</asp:Button>
</div>

说明:如果页面上有多个Validator控件,并且多个Button需要进行分组校验,则只需要设置Button的ValidationGroup即可。
上面的这个方法,貌似已经完美了;通常情况下,文章写到这里也该结束了,我们的系统也是这么使用的。但是……最近一个客户,老抱怨每个月总有几条重复的业务数据。。
. 当用户的点击速度足够快……
根据客户的反馈信息,我们查了下那几条重复的业务数据,发现同样的记录偶尔会出现两次,并且DB中记录的创建时间完全相等(精确到毫秒)。然后也让用户的演示了一下她日常的操作方式,发现她点鼠标相当的快;虽然没有重现问题,但我们也大致知道问题的可能原因了:当用户点击的速度足够快,浏览器还没有来得及将button禁用掉,用户又点击了第二次……
为了重现问题,我们做了如下测试:
ClickMe 
代码如下:


<script type="text/javascript">
function disableButton(button) //, validateGroup)
{
if (typeof (Page_ClientValidate) == 'function' && Page_ClientValidate() == false)
{
return false;
}
button.disabled = true;
}

function doubleClick()
{
var button1 = document.getElementById('btnPostBack1');
button1.onclick();
button1.onclick(); //模拟重复点击2次
}
</script>


<div>
<asp:TextBox runat="server" ID="tbxInput1"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="tbxInput1"
ValidationGroup="Group1" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:Button runat="server" ID="btnPostBack1" Text="按我1" UseSubmitBehavior="false"
ValidationGroup="Group1" OnClientClick="disableButton(this);" OnClick="btnPostBack_Click">
</asp:Button>
</div>
<input type="button" onclick="doubleClick();" value="点击一次,模拟连续点击'按我1'两次" />

然后页面后台代码如下:
代码如下:


public static int i = 0;
protected void btnPostBack_Click(object sender, EventArgs e)
{
Response.Write(((Button)sender).Text + " " + (++i).ToString());//将累加结果
输出
}

每次页面PostBack时,全局变量+1,如果连续PostBack两次,则得到的是+2的累计。测试的结果:除了偶尔+1,大部分情况都是累加2……看来,如果用户点击鼠标的速度足够快,前面2中的办法还是无法杜绝重复提交,咋办呢???
4. 终极绝杀技

ClickMe2

    处理思路就是,用数组记录用户的点击时间,如果点击时间小于某个时间间隔(下面演示的1秒,即1000毫秒),则认为是重复提交,并取消当前点击事件,直接上代码了:
代码如下:


<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript">
var date = new Array();
function disableButton(button, validateGroup)
{
date.push(new Date());
if (date.length > 1
&& (date[date.length - 1].getTime() - date[date.length - 2].getTime() < 1000))//小于1秒则认为重复提交
{
event.cancelBubble = true; //测试时发现,如果直接单独设置cancelBubble、或者returnValue来取消事件,经常取消不了,依然存在重复提交的可能。因此只好用返回值来实现手工控制!
return false;
}
if (typeof (Page_ClientValidate) == 'function'
&& ((validateGroup == undefined && Page_ClientValidate() == false)
|| (validateGroup != undefined && Page_ClientValidate(validateGroup) == false)))
{ //如果指定了ValidateGroup,则只验证改组;如果未指定,则验证全部验证控件
return false;
}

button.disabled = true;
return true;
}

function doubleClick()
{
var button1 = document.getElementById('btnPostBack1');
button1.onclick();
button1.onclick();
}
</script>
</head>
<body>
<form id="form" runat="server">
<div>
<asp:TextBox runat="server" ID="tbxInput1"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="tbxInput1"
ValidationGroup="Group1" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:Button runat="server" ID="btnPostBack1" Text="按我1" UseSubmitBehavior="false"
ValidationGroup="Group1" OnClientClick="if(!disableButton(this, 'Group1')) return false;" OnClick="btnPostBack_Click">
</asp:Button>
</div>
<div>
<asp:TextBox runat="server" ID="tbxInput2"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="tbxInput2"
ValidationGroup="Group2" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:Button runat="server" ID="btnPostBack2" Text="按我2" UseSubmitBehavior="false"
OnClientClick="if(!disableButton(this)) return false;" OnClick="btnPostBack_Click">
</asp:Button>
</div>
<input type="button" onclick="doubleClick();" value="点击一次,模拟连续点击'按我1'两次" />
</form>
</body>
</html>

说明:
(1). 测试时发现,如果直接单独设置cancelBubble、或者returnValue来取消事件,经常取消不了,依然存在高频率的重复提交。因此只好用返回值来实现手工控制;
(2). disableButton接收一个validateGroup参数,如果指定了ValidateGroup,则只验证改组;如果未指定,则验证全部验证控件。

下载本文
显示全文
专题