В контроллерах рельсов, как предотвратить двойную отправку (когда пользователь дважды нажимает кнопку отправки или нажимает клавишу ввода дважды)? - PullRequest
13 голосов
/ 01 июля 2010

Ну, все в заголовке, но я объясню немного больше: -)

В моем приложении rails есть много форм (Ajaxified или нет).

Чтобы пользователи не могли отправлять дваждыили более некоторые формы, я использую Javascript.

Есть мой сценарий для Ajaxified формы:

  • пользователь отправляет форму (щелчок или ввод)
  • javascriptотключить кнопку отправки
  • контроллер rails выполняет какие-либо действия (например, запрос Soap или вставка в БД)
  • контроллер rails обновляет страницу и при необходимости активирует кнопку отправки (в случаеошибки)

Теперь Я хочу добавить серверный код , чтобы сохранить чистоту, если пользователь обходит javascript.

Есть предложения?

Ответы [ 8 ]

8 голосов
/ 23 ноября 2013

Я использую 4 метода для 4 сценариев, во-первых, пожалуйста, предпочтите мой awnser здесь: Запретить двойную отправку в форме Rails AJAX

ограничение только на клики для пользователей:

используйте stopImmediatePropagation и добавьте событие click к целевому домену.

  /**
   * 防止按钮重复点击。
   * 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.

  /**
   * 使用:
   *   在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" %>

или: отключить : 仅仅 对 表单 元素 , 按钮 起作用 , , 阻止 的 的 事件 触发

<input type="submit" value="submit" />
<input type="button" value="button" />
<input type="image" value="image" />

другие:

Этоэто драгоценный камень: https://github.com/mlanett/redis-lock

Redis.current.lock("#{current_user.id}.action_name") do
   # Some code
end
7 голосов
/ 17 июля 2012

Вы можете добавить параметр: disable_with => «Please Wait ...» к тегу отправки.

5 голосов
/ 20 февраля 2013

Попробуйте использовать блокировку Redis и окружите свой блок чем-то вроде

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

Это драгоценный камень, который я использую https://github.com/mlanett/redis-lock

2 голосов
/ 05 июля 2010

Вот что я бы сделал:

  1. Добавьте поле "токен" к объекту в БД, который должен быть изменен.
  2. Поместите этот токен в форму, которая изменяетуказанный объект.
  3. Сразу после модификации сохраните этот объект с НОВЫМ токеном.
  4. Используйте этот токен в других просмотрах страницы.

Это не предотвратит двойную передачу, нопо крайней мере, предотвратит изменения со второго коммита.Т.е. когда пользователь отправляет форму во второй раз, код будет проверять представленный токен по сравнению с токеном в базе данных, а если они не совпадают - не обновлять объект.

Это также имеет откат для вновьсозданные объекты (например, если пользователь хочет создать комментарий или что-то подобное).Но в этом случае вы можете захотеть проверить интервал времени создания от того же пользователя, и если он меньше, скажем, 5 секунд - вы не допустите, чтобы объект был «создан».

1 голос
/ 01 июля 2010

Не реальное предложение (я не удивлюсь отрицательным голосам), но будет ли ваш сайт работать без JS?Как насчет показа ему соответствующего сообщения, что для нормальной работы ему нужно включить JS, иначе вы не будете показывать ему много-много форм на странице.

0 голосов
/ 15 мая 2018

Я тоже столкнулся с той же проблемой, и я решил ее очень простым способом (может быть, это неправильно или существует что-то глючное, чего я не заметил, пожалуйста, сообщите мне, если вы узнали)

Вот мой ответ:

$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!!

Самая большая проблема, с которой я столкнулся, заключается в том, что если я просто отключу кнопку отправки сразу после нажатия на нее, запрос на отправку не будет отправлен, и страница будет просто зависать,ничего не происходит.

Я думаю, причина в том, что атрибут "disabled" не позволяет отправить запрос на отправку (хотя я понятия не имею, каковы отношения между этими двумя ...)

Итак, я думаю событие отключения должно быть выполнено после события submit.

Насколько я понимаю, отправка формы - это событие Javascript, а setTimeout - асинхронный метод.Поскольку Javascript выполняется на основе цикла событий , событие ajax будет помещено в конец квенэ события и будет выполнено только после завершения всех событий синхронизации.

В моем кодепосле первого щелчка кнопка отправки будет отключена через 0 миллисекунд, что невозможно для человека щелкнуть во второй раз, проблема решена!

0 голосов
/ 05 июля 2010

Не уверен, что это полезно:

ON SERVERSIDE:

  1. При новой загрузке формы установите сеанс ['variable'] = false //то есть форма еще не отправлена.

  2. При отправке формы проверьте:

    if session['variable'] == true
    {
       do nothing...
    }
    else
    {
       set session['variable'] = true;
      //do submit logic here
    }
    
0 голосов
/ 01 июля 2010

1) Приятно также показать некоторый движущийся индикатор, чтобы сообщить пользователю, что что-то происходит, что его запрос обрабатывается. Это должно устранить множество двойных представлений.

2) Если пользователь отключил JavaScript, как вы собираетесь отправлять «ajaxified» формы? Если сайт не работает без javascript, то, вероятно, лучше просто уведомить пользователя (как предлагает Eimantas ).

редактировать Один из примеров такого индикатора, просто чтобы было понятно, что я имею в виду под 1.
http://www.netzgesta.de/busy/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...