DF-АВТ-007 · Автоматизация процессов
Как защитить форму от повторной отправки заявки
- Почему одной блокировки кнопки недостаточно
- Как присвоить отправке одноразовый ключ
- Где остановить дубль на сервере и в базе
- Какими проверками испытать защиту
Покупатель заполнил форму, нажал кнопку и не увидел ответа. Он нажимает ещё раз — в CRM появляются две одинаковые заявки, менеджеры независимо звонят одному человеку, а уведомления отправляются дважды.
Блокировка кнопки уменьшает число повторных нажатий, но защищать запись нужно на сервере. Для каждой попытки заполнения форма получает уникальный ключ. Если запрос с таким ключом уже обработан, сервер возвращает прежний результат, а не создаёт новую заявку.
Эту защиту лучше включить в автоматизацию приёма заявок вместе с правилами передачи данных и обработки ошибок. Требование можно заранее закрепить в техническом задании на автоматизацию: один ключ отправки создаёт не более одной заявки.
Откуда берутся повторные отправки?
Очевидный случай — двойной щелчок. Но тот же запрос может повториться после зависания страницы, обновления вкладки или автоматической попытки клиента восстановить соединение. Сервер иногда успевает записать заявку, хотя ответ до браузера не доходит. Пользователь видит ошибку и отправляет форму заново.
Обычная форма часто передаёт данные методом POST. По семантике HTTP этот метод сам по себе не считается идемпотентным: повтор одинакового запроса может ещё раз изменить состояние сервера. Поэтому безопасное повторение нужно проектировать отдельно.
Отключить кнопку после начала отправки всё равно стоит. Стандарт HTML определяет атрибут disabled как способ сделать элемент неактивным. Это улучшает поведение интерфейса, однако не заменяет серверную проверку: запрос можно повторить из другой вкладки, программы или после сетевой ошибки.
Сравнивать только имя, телефон и текст обращения тоже опасно. Один человек вправе отправить два разных обращения с одинаковыми контактами. Защита должна отличать повтор одной отправки от новой заявки с похожими данными.
Как работает ключ отправки?
Когда пользователь открывает пустую форму, система создаёт случайный идентификатор, например UUID. Он хранится в скрытом поле или передаётся в заголовке запроса. Пока пользователь исправляет ошибку или повторяет неудавшуюся отправку, ключ остаётся прежним. Для новой формы нужен новый ключ.
Проект IETF об Idempotency-Key описывает этот принцип для HTTP API: клиент передаёт уникальное значение, а сервер узнаёт по нему повтор запроса. Документ на 30 октября 2026 года остаётся проектом стандарта, поэтому имя заголовка и правила хранения необходимо явно зафиксировать в своей системе.
Сервер сначала ищет ключ в журнале обработанных запросов. Если записи нет, он сохраняет заявку и связывает с ней ключ. Если запись уже есть, возвращает идентификатор ранее созданной заявки. Повтор не должен заново запускать уведомления, создание сделки или другие последствия.
Один ключ нельзя применять к разным наборам данных. Если ключ совпал, а содержимое формы изменилось, серверу безопаснее отклонить запрос как конфликт. Иначе старая заявка может выглядеть успешно принятой после того, как пользователь заменил телефон, услугу или комментарий.
Где поставить окончательную защиту?
Проверки в прикладном коде недостаточно. Два одинаковых запроса способны прийти почти одновременно: оба проверят, что ключа ещё нет, и оба попробуют создать запись. Поэтому уникальность ключа нужно закрепить ограничением в базе данных.
Документация PostgreSQL указывает, что ограничение UNIQUE не допускает одинаковые значения в выбранном столбце или сочетании столбцов. Для формы таким сочетанием могут быть источник и ключ отправки. Это позволяет использовать одинаковые форматы ключей в разных каналах, не смешивая их записи.
Создание заявки и фиксацию ключа выполняют как одну операцию с точки зрения системы. Если уникальность уже нарушена, обработчик не создаёт ещё одну запись, а находит существующую и возвращает её результат. Пользователь при этом получает понятное подтверждение, а не предложение повторить действие вслепую.
Отдельно определите момент запуска последующих действий. Уведомление менеджеру, запись в CRM и письмо клиенту должны быть привязаны к идентификатору принятой заявки. Иначе основная запись останется единственной, но письмо или задача всё равно появятся дважды.
Как проверить защиту до запуска?
Начните с обычной отправки: должна появиться одна заявка и один набор связанных действий. Затем быстро нажмите кнопку несколько раз. После этого повторите тот же запрос с тем же ключом напрямую через тестовый клиент — результатом должен остаться прежний идентификатор заявки.
Следующая проверка имитирует потерянный ответ. Сервер принимает запрос, но клиент не получает подтверждение и повторяет его с тем же ключом. В базе по-прежнему должна быть одна заявка, а повторный ответ должен сообщить о ранее выполненной операции.
Проверьте и одновременные запросы. Два запроса с одним ключом отправляют без паузы. Один создаёт запись, второй получает существующий результат либо временный ответ с предложением повторить проверку позже. Две заявки означают, что между проверкой и записью осталась гонка.
Наконец, отправьте изменённые данные со старым ключом, а затем исходные данные с новым. Первый случай должен завершиться конфликтом, второй — создать отдельную заявку. Так проверяется главное правило: повтор одной операции не размножает записи, а новое обращение не теряется из-за внешнего сходства.
Источники
Deskforge на связи
Где нас найти
Нажмите на карточку или наведите камеру на QR-код.