Предотвращение повторной отправки формы

107

Первая страница содержит HTML-форму. Вторая страница - код, обрабатывающий отправленные данные.

Форма на первой странице будет отправлена. Браузер перенаправляется на вторую страницу. Вторая страница обрабатывает отправленные данные.

На этом этапе, если страница 2 будет обновлена, появится предупреждение «Подтвердить повторную отправку формы».

Можно ли это предотвратить?

Эмануил Русев
источник
Используйте диалоговое окно подтверждения: stackoverflow.com/questions/6457750/form-confirm-before-submit/…
3
используйте post redirect get, как описано здесь - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer
Вы можете предотвратить это даже без перенаправления. Смотри сюда
Евгений Коньков
1
Возможный дубликат Как предотвратить
Евгений Коньков

Ответы:

145

Здесь есть 2 подхода:

Метод 1: используйте AJAX + Redirect

Таким образом, вы публикуете свою форму в фоновом режиме с помощью JQuery или чего-то подобного Page2, в то время как пользователь по-прежнему видит отображаемую page1. После успешной публикации вы перенаправляете браузер на Page2.

Метод 2: публикация + перенаправление на себя

Это распространенная техника на форумах. Форма на странице 1 отправляет данные на страницу 2, страница 2 обрабатывает данные и делает то, что нужно сделать, а затем выполняет перенаправление HTTP на себя. Таким образом, последнее «действие», которое запоминает браузер, - это простой GET на странице 2, поэтому форма не будет повторно отправлена ​​после F5.

CodeTwice
источник
2
Вариант 2 способа - перенаправление на другую страницу. Например, вы отправляете POST на example.com/save.html, а после сохранения вы перенаправляете на example.com/list.html .
Гийом,
1
В методе 1 я бы использовал плагин jQuery с именем BlockUI, чтобы клиент знал, что что-то происходит.
Shikiryu,
Я использую метод 2, но он просит меня повторно отправить его, если я обновлю его. У кого-нибудь есть подобная проблема?
острый
Вы уверены, что перенаправление HTTP работает правильно? Откройте сетевой инспектор / firebug / все, что есть в вашем браузере, для проверки исходящих HTTP-запросов и проверьте HTTP-вызовы. Вы должны увидеть, как первый (отправка данных формы) происходит с методом POST, возвращая HTTP-код 301 с заголовком Location, указывающим на самого себя, а затем сразу же вы должны увидеть другой HTTP-запрос на ту же страницу с методом GET.
CodeTwice
@CodeTwice: В моем случае page1 отправляет на page2, page2 обрабатывает данные и показывает страницу (со значениями, передаваемыми в шаблон) .. Где должно происходить перенаправление? .. На странице нет запросов GET.
user1050619 07
24

Вам нужно использовать шаблон PRG - Post / Redirect / Get, и вы только что реализовали P PRG. Вам нужно перенаправить . (В наши дни вам вообще не нужно перенаправление. См. Это )

PRG - это шаблон дизайна веб-разработки, который предотвращает дублирование отправки формы, что означает: форма отправки (запрос публикации 1) -> перенаправление -> получение (запрос 2).

Under the hood

Код состояния перенаправления - HTTP 1.0 с HTTP 302 или HTTP 1.1 с HTTP 303

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

Код состояния перенаправления гарантирует, что в этой ситуации браузер веб-пользователя может безопасно обновить ответ сервера, не вызывая повторной отправки начального запроса HTTP POST.

Double Submit Problem

Проблема двойной отправки

Post/Redirect/Get Solution

Опубликовать / Перенаправить / Получить решение

Источник

Ангелин Надар
источник
2
Хорошее объяснение. Мне интересно, что произойдет, если пользователь нажмет кнопку «Назад» в браузере после выполнения перенаправления. Будет ли снова отображаться всплывающее окно «Подтвердить повторную отправку формы»? Я предполагаю, что даже если всплывающее окно подтверждения не отображается снова, запрос на публикацию на странице 1 с теми же данными будет выполнен снова, и снова пользователь будет перенаправлен на страницу 2. У меня есть ряд форм, form1, form2, form3. как можно без проблем вернуться из формы "n" в форму "n-1". Случайный вопрос: где вы изучали шаблон проектирования PRG
Rpant
@Rpant на chrome файл php, который отправляет заголовок местоположения, даже не отображается в истории браузера, поэтому кнопка возврата просто возвращает вас к форме.
FluorescentGreen5
@Angelin Как получить данные после перенаправления? Например, когда я отправляю некоторые данные в / some_url и перенаправляю на / some_url, который делает запрос GET. Как мне узнать, каким должен быть ответ, поскольку / some_url является общим и не содержит идентификатор, например / some_url / <id>?
Амит Трипати,
@AmitTripathi вы должны создать сами. например: запрос POST может включать вставку данных клиента, тогда как GET будет отображать успешно вставленные данные. Вы сможете показать данные, поскольку это были не что иное, как данные формы, или повторно использовать представление страницы клиента, извлекая из базы данных для этого клиента идентификатор клиента, который вы получили после успешной вставки в базу данных.
Ангелин Надар,
Но я согласен с решением @Eugen Konkov
Надар
10

Напрямую нельзя, и это хорошо. Предупреждение браузера есть не просто так. Эта ветка должна ответить на ваш вопрос:

Запретить кнопку "Назад" отображать предупреждение о подтверждении POST

Было предложено два ключевых обходных пути: шаблон PRG и отправка AJAX с последующим перемещением сценария.

Обратите внимание: если ваш метод позволяет использовать GET, а не метод отправки POST, то это решит проблему и лучше согласуется с соглашением. Эти решения предоставляются при условии, что вы хотите / нуждаетесь в данных POST.

Дэвин
источник
8

Единственный способ быть на 100% уверенным, что одна и та же форма никогда не будет отправлена ​​дважды, - это встроить уникальный идентификатор в каждую форму, которую вы отправляете, и отслеживать, какие из них были отправлены на сервер. Проблема здесь в том, что если пользователь выполняет резервное копирование на страницу, где была форма, и вводит новые данные, та же форма не будет работать.

Blrfl
источник
6

Ответ состоит из двух частей:

  1. Убедитесь, что повторяющиеся сообщения не портят ваши данные на стороне сервера. Для этого вставьте в сообщение уникальный идентификатор, чтобы вы могли отклонять последующие запросы на стороне сервера. В терминах обмена сообщениями этот шаблон называется идемпотентным получателем .

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

    • перенаправление на GET после POST (шаблон GET перенаправления POST)
    • отключение кнопки с помощью javascript

Ничего из того, что вы делаете в соответствии со вторым этапом, не предотвратит дублирование отправки. Люди могут нажимать очень быстро, а хакеры в любом случае могут публиковать сообщения. Вам всегда нужен 1. если вы хотите быть абсолютно уверены в отсутствии дубликатов.

Айвейн
источник
2

Если вы обновите страницу с данными POST, браузер подтвердит вашу повторную отправку. Если вы используете данные GET, сообщение отображаться не будет. Вы также можете перенаправить вторую страницу после сохранения отправки на третью страницу без данных.

Джоэл
источник
2

Я обнаружил, что никто не упоминал об этой уловке.

Без перенаправления вы все равно можете предотвратить подтверждение формы при обновлении.

По умолчанию код формы такой:

<form method="post" action="test.php">

теперь измените его на <form method="post" action="test.php?nonsense=1">

Вы увидите волшебство.

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

Выделить
источник
2

Вы можете сделать это с помощью jquery

<script>
   $(document).ready(function(){
   window.history.replaceState('','',window.location.href)
   });
</script>

Это самый элегантный способ предотвратить повторную отправку данных после отправки из-за обратной публикации.

Надеюсь это поможет.

нубпрограммист
источник
0

Шаблон PRG может предотвратить только повторную отправку, вызванную обновлением страницы. Это не 100% безопасная мера.

Обычно я предпринимаю следующие действия, чтобы предотвратить повторную отправку:

  1. На стороне клиента - используйте javascript, чтобы предотвратить повторяющиеся нажатия на кнопку, которая запускает отправку формы. Вы можете просто отключить кнопку после первого нажатия.

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

В большинстве случаев эти меры могут помочь предотвратить повторную отправку.

ehe888
источник
0

Мне очень нравится ответ @Angelin. Но если вы имеете дело с устаревшим кодом, где это нецелесообразно, этот метод может сработать для вас.

Вверху файла

// Protect against resubmits
if (empty($_POST))  {
   $_POST['last_pos_sub'] = time();
} else {
     if (isset($_POST['last_pos_sub'])){
        if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
           redirect back to the file so POST data is not preserved
        }
        $_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
     }
}

Затем в конце формы наклеить last_pos_subтак:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Скотт К. Уилсон
источник
0

Попробуйте трис:

function prevent_multi_submit($excl = "validator") {
    $string = "";
    foreach ($_POST as $key => $val) {
    // this test is to exclude a single variable, f.e. a captcha value
    if ($key != $excl) {
        $string .= $key . $val;
    }
    }
    if (isset($_SESSION['last'])) {
    if ($_SESSION['last'] === md5($string)) {
        return false;
    } else {
        $_SESSION['last'] = md5($string);
        return true;
    }
    } else {
    $_SESSION['last'] = md5($string);
    return true;
    }
}

Как использовать / пример:

if (isset($_POST)) {
    if ($_POST['field'] != "") { // place here the form validation and other controls
    if (prevent_multi_submit()) { // use the function before you call the database or etc
        mysql_query("INSERT INTO table..."); // or send a mail like...
        mail($mailto, $sub, $body); // etc
    } else {
        echo "The form is already processed";
    }
    } else {
    // your error about invalid fields
    }
}

Шрифт: https://www.tutdepot.com/prevent-multiple-form-submission/

Ромуло ZC Cunha
источник
0

используйте js для предотвращения добавления данных:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Тран Ань Хиен
источник