Избегайте условий гонки в PHP на отправке: Пожалуйста, не нажимайте кнопку «Отправить» более одного раза!

Некоторое время назад, в онлайн-приложениях говорилось: «Не нажимайте кнопку« Отправить »более одного раза». Это уже не так, верно? Как вы защищаете это, скажем, PHP?

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

Редактировать: Спасибо всем за решение Javascript. Все в порядке, но это немного работа. 1) Это тип ввода = изображение и 2) Подача должна продолжать стрельбу, пока материал Spry не говорит, что все в порядке. Это редактирование, просто я жалуюсь, в основном, так как я предполагаю, что, посмотрев на материал Spry, я смогу понять это.

Изменить: не то, чтобы кто-то мог интегрироваться с материалом Spry, но вот мой последний код, используя Prototype для document.getElementByid. Комментарии приветствуются!

function onSubmitClick() { var allValid = true; var queue = Spry.Widget.Form.onSubmitWidgetQueue; for (var i=0;i<queue.length; i++) { if (!queue[i].validate()) { allValid = false; break; } } if (allValid) { $("theSubmitButton").disabled = true; $("form").submit(); } } 

По какой-то причине необходима вторая подача формы …

Вы должны выполнять защиту как на стороне клиента, так и на стороне сервера.

Клиентская кнопка – отключить, например, с помощью jquery, как описано cletus.

Серверная сторона – помещает токен в форму. Если есть два представления с одним и тем же токеном, игнорируйте последнее. Используя этот подход, вы защищены от CSRF .

Это отличный пример того, чем полезен jQuery (вы можете сделать это в любом Javascript). Добавьте этот код:

 $("form").submit(function() { $(":submit",this).attr("disabled", "disabled"); }); 

И отключает кнопки отправки один раз щелкнув один раз.

Как отмечали другие, вы можете отключить кнопку. Мне больше нравятся проверки на стороне сервера, но JS может быть отключен, пользователь может нажать обновление (хотя, если вы правильно используете POST, который будет генерировать предупреждение) и т. Д.

Вы можете добавить временную метку к форме и отследить ее в сеансе – требуется, чтобы отметка времени POST была больше, чем отслеживаемая. Это предотвратит большинство двойных сообщений без заметного влияния на пользовательский интерфейс.

Также важно отметить, что поведение PHP по умолчанию, если оно обнаруживает пользователя, «отменило» запрос (закрыв браузер, нажав «остановить» или, возможно, нажав «Отправить второй раз»), прекратите выполнение сценария. Это нежелательно, если вы выполняете какую-то длительную транзакцию. Подробнее здесь .

Я бы подумал, что лучший вариант – позволить скрипту PHP сначала установить флаг в массиве сеансов, чтобы указать, что он обрабатывает форму. Таким образом, второй запрос может быть настроен так, чтобы дождаться завершения первоначального запроса (используйте сервер вызова сна, ожидающий сброса флага).

Важно не слишком сильно мешать кнопке отправки и процессу, потому что характер Интернета является неопределенным; пользователь может захотеть щелкнуть второй раз, если ответа не поступило, и вы не хотите потерять свои данные. Это может произойти, если первый запрос потерян, и вы отключили единственный способ отправить (и, следовательно, хранить) данные.

Одно чистое решение должно отправить запрос и использовать javascript для отображения сообщения «Обработка», чтобы пользователь мог видеть, что что-то происходит, но им не мешают повторно отправлять данные.

Одним из решений является немедленное отключение кнопки при нажатии с помощью Javascript. Это, очевидно, зависит от того, работает ли javascript в клиентском браузере.

Уловка на стороне сервера лучше, так как она будет ловить условия гонки между несколькими окнами браузера, если пользователь дважды редактирует одну и ту же запись.

Я простую версию этого с javascript, когда я работал с ASP.NET AJAX, но он должен работать в любом случае, когда ваша кнопка имеет фактический идентификатор.

Я делаю следующие шаги в событии onclick:

  1. Отключить кнопку, которая вызвала событие onclick
  2. Храните идентификатор кнопки в closureId магической переменной. Я могу ссылаться позже через закрытие
  3. Используйте функцию setTimeout для выполнения динамически определенного обратного вызова после указанного количества миллисекунд (5000 мс = 5 секунд)
  4. В функции обратного вызова я могу ссылаться на magic closureId и снова включить кнопку после таймаута

Ниже приведена простая кнопка HTML, которую вы можете вставить в файл test.html самостоятельно:

<input id = "btnTest" type = "button" value = "test" onclick = "var closId = this.id; this.disabled = true; setTimeout (function () {document.getElementById (closedId) .disabled = false; }, 5000);>

Вы по-прежнему должны выполнять проверки на стороне сервера, как это предлагали другие, но в моем собственном экземпляре submit также выполнил проверку бизнес-логики на стороне сервера. Пользователь должен представить свою форму, а затем ждать, чтобы увидеть, действительно ли бизнес-логика на стороне сервера разрешит эту запись или нет. Это также помогло во время разработки, когда я мог забрать мой сервер и до сих пор не удается подать заявку.

Заметьте, что я вызываю, что closureId переменную «magic», потому что у меня нет четкого понимания того, как она работает. Я просто понял, что вызов this.id не работает, потому что this ссылка на функцию таймаута, которая выполняет динамический функция обратного вызова и не имеет никакого идентификатора DOM.

Я не мог найти другого способа получить ссылку на исходное событие (this.this.id не работает), но лексическое охват каким-то образом позволяет мне получить доступ к переменной closId, как это было определено во время оригинала нажмите кнопку.

Не стесняйтесь исправлять / комментировать, если вы знаете лучший способ!

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