在rails控制器中,如何防止双重提交(当用户双提交按钮或按两次输入时)?

好吧,一切都在标题中,但我会解释一下:-)

我的rails应用程序包含许多表单(Ajaxified或不Ajaxified)。

为了防止用户提交两次或更多的表格,我使用Javascript。

这是一个Ajaxified表单的场景:

  • 用户提交表单(clic或enter)
  • javascript禁用提交按钮
  • rails控制器执行操作(如Soap请求或DB中的插入)
  • rails控制器更新页面并在必要时启用提交按钮(如果有错误)

现在我想添加服务器端代码,以便在用户绕过javascript时保持真正干净。

有什么建议?

尝试使用Redis锁定并用类似的东西包围你的块

Redis.current.lock("#{current_user.id}.action_name") do # Some code end 

这是我正在使用的gemhttps://github.com/mlanett/redis-lock

我在4个场景中使用4方法,请首先在这里更喜欢我的awnser: 防止在Rails AJAX表单中提交双重提交

仅针对用户的点击限制:

使用stopImmediatePropagation并将click事件添加到目标dom。

  /** * 防止按钮重复点击。 * NOTICE: #1 需要在作用点之前调用此方法 #2 stopImmediatePropagation 会阻止后面的所有事件包括事件冒泡* @delay_duration 两次点击的间隔时间*/ $.fn.preventMultipleClick = function (delay_duration) { delay_duration = delay_duration || 3000; var last_click_time_stamp = 0; var time_duration = 0; $(this).bind('click', function (event) { time_duration = last_click_time_stamp ? event.timeStamp - last_click_time_stamp : 0; //console.debug("preventMultipleClick", last_click_time_stamp, time_duration); if (time_duration && time_duration < delay_duration) { event.stopImmediatePropagation(); } else { //console.debug("skip preventMultipleClick~"); last_click_time_stamp = event.timeStamp; } }); }; 

限制提交,如ajax:

使用ajax的beforeSend attribut。

  /** * 使用: * 在jquery的ajax方法中加入参数:beforeSend * 例如:beforeSend: function(){return $.preventMultipleAjax(event, 5000)} * * @param event * @param delay_duration * @returns {boolean} */ $.preventMultipleAjax = function (event, delay_duration) { delay_duration = delay_duration || 3000; var target = $(event.target); var last_click_time_stamp = target.attr("_ajax_send_time_stamp") || 0; var time_duration = last_click_time_stamp ? event.timeStamp - last_click_time_stamp : 0; //console.debug("preventMultipleAjax", last_click_time_stamp, time_duration); if (time_duration && time_duration < delay_duration) { return false; } else { //console.debug("skip preventMultipleAjax~"); target.attr("_ajax_send_time_stamp", event.timeStamp); return true; } }; 

仅适用于表格:

 <%= f.submit "Save annotation", :disable_with => "Saving...", :class => "btn btn-primary", :id => "annotation-submit-button" %> 

或:禁用:仅仅对表单元素,按钮等起作用,会阻止其上的事件触发

    

其他:

这是gem: https : //github.com/mlanett/redis-lock

 Redis.current.lock("#{current_user.id}.action_name") do # Some code end 

您可以将选项:disable_with =>“Please Wait …”添加到提交标记。

这是我要做的:

  1. 将“标记”字段添加到将要更改的db中的对象。
  2. 将该标记放入修改所述对象的表单中。
  3. 修改后立即使用NEW令牌保存该对象。
  4. 在其他页面视图中使用该标记。

这不会阻止双重提交,但至少会阻止第二次提交的更改。 即,当用户第二次提交表单时,代码将针对数据库中的表单检查提交的令牌,如果它们不匹配,则不要对对象进行更新。

这也为新创建的对象提供了一个缺点(即,如果用户想要创建一个注释或类似的smth)。 但在这种情况下,您可能需要检查来自同一用户的创建时间间隔,如果它小于,比如5秒 – 您将阻止该对象被“创建”。

这不是一个真正的建议(我不会对downvotes感到惊讶),但是你的网站在没有JS的情况下仍然可用吗? 如何向他展示适当的消息,正常操作需要启用JS,否则你不会在页面上显示很多很多表格。

1)很高兴也显示一些移动指示器,让用户知道正在发生的事情,他们的请求正在处理中。 它应该消除许多双重提交。

2)如果用户已禁用javascript,您将如何提交“ajaxified”表单? 如果网站在没有javascript的情况下变得无法运行,那么最好只是通知用户(就像Eimantas建议的那样)。

编辑这种指标的一个例子,只是为了清楚我在1中的意思。
http://www.netzgesta.de/busy/

不确定这是否有用:

在服务器上:

  1. 在表单的新加载时,设置会话[‘variable’] = false //意味着表单尚未提交。

  2. 在表单提交上,检查:

     if session['variable'] == true { do nothing... } else { set session['variable'] = true; //do submit logic here } 

我碰巧也遇到了同样的问题,我用一种非常简单的方式解决了它(也许它不合适或者存在我没注意到的有问题的东西,如果你发现的话请通知我)

这是我的答案:

 $submitButton.on('click', function(e) { setTimeout(() => { $(this).attr('disabled', ''); }, 0); }); 

我面临的主要问题是,如果我双击原始按钮,它会提交两次请求并导致某些不可预测的内容,因此我尝试在单击后立即阻止具有“禁用”属性的按钮。 这是我写的。

 // !!this is an incorrect example!! $submitButton.on('click', function(e) { $(this).attr('disabled', ''); }); // !!don't copy this!! 

我面临的最大问题是,如果我只是在点击后立即禁用提交按钮,提交请求将不会提交,页面将只挂在那里,因为什么都没发生。

我认为原因是“禁用”属性阻止了提交请求的提交。(虽然我不知道这两者之间的关系是什么……)

所以,我认为应该在提交事件后执行disable事件。

据我所知,表单提交是一个Javascript事件,而setTimeout是一个异步方法。 当Javascript基于事件循环执行时,ajax事件将被放在事件quene的末尾,并且只有在所有同步事件完成后才会执行。

在我的代码中,第一次点击后,提交按钮将在0毫秒后被禁用,这对于人类第二次点击是不可能的,问题解决了!