Запобігання повторної подачі форми


107

Сторінка перша містить форму HTML. Сторінка друга - код, який обробляє подані дані.

Форма на сторінці перша надсилається. Браузер перенаправляється на другу сторінку. Сторінка дві обробляє подані дані.

У цей момент, якщо друга сторінка оновлюється, з’являється повідомлення "Підтвердити повторну подачу форми".

Чи можна цього запобігти?


Використовуйте діалог підтвердження: stackoverflow.com/questions/6457750/form-confirm-before-submit / ...

3
використовувати після редиректу отримати як описано тут - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer

Ви можете запобігти цьому навіть без перенаправлення. Подивіться тут
Євген Конков

Відповіді:


145

Тут використовувались два підходи:

Спосіб 1: Використовуйте перенаправлення AJAX +

Таким чином ви розміщуєте форму у фоновому режимі, використовуючи JQuery або щось подібне до Page2, тоді як користувач все ще бачить сторінку1. Після успішної публікації ви перенаправляєте браузер на сторінку2.

Спосіб 2: Опублікувати + Переспрямувати себе

Це поширена техніка на форумах. Форма на Page1 розміщує дані на сторінці Page2, Page2 обробляє дані та робить те, що потрібно зробити, а потім перенаправляє HTTP на себе. Таким чином, остання «дія», яку пам’ятає браузер, - це простий GET на сторінці2, тому форма не надсилається повторно після F5.


2
Варіант способу 2 - це переспрямування на іншу сторінку. Наприклад, ви розміщуєте POST на example.com/save.html і після збереження виконується переадресація на example.com/list.html .
Гійом

1
За допомогою методу 1 я б використовував плагін jQuery під назвою BlockUI, щоб клієнт знав, що щось відбувається.
Шикірю

Я використовую метод 2, але він просить мене знову надіслати його, якщо оновити його. Хтось має подібну проблему до цього?
захопливий

Ви впевнені, що перенаправлення HTTP працює належним чином? Відкрийте інспектор мережі / firebug / що б не було у вашому веб-переглядачі для перевірки вихідних HTTP-запитів та перевірки HTTP-дзвінків. Вам слід побачити перший (розміщення даних форми), що відбувається за допомогою методу POST, повернення HTTP-коду 301 із заголовком Location, що вказує на себе, а потім негайно вам слід побачити ще один HTTP-запит на ту ж сторінку методом GET.
CodeTwice

@CodeTwice: У моєму випадку сторінка1 публікує сторінку2, сторінка2 обробляє дані та показує сторінку (зі значеннями передаються до шаблону) .. Куди має відбутися перенаправлення? .. На сторінці немає GET запитів.
user1050619

24

Вам потрібно використовувати PRG - Post / Redirect / Get pattern, і ви щойно реалізували 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 on chrome файл php, який надсилає заголовок місцезнаходження, навіть не відображається в історії браузера, тому кнопка "назад" просто поверне вас до форми.
FluorescentGreen5

@Angelin Після перенаправлення, як отримати отримані дані? Наприклад, коли я публікую деякі дані в / some_url і перенаправляю на / some_url, що робить GET-запит. Як я можу знати, якою має бути відповідь, оскільки / some_url є загальним, і не конатує ідентифікатор, як / some_url / <id>?
Аміт Тріпаті

@AmitTripathi ви повинні створити себе. наприклад: POST-запит може містити дані клієнта, тоді як GET буде відображати введені дані успішно. Ви зможете показати ці дані, оскільки це були не що інше, як дані форми, або повторно використовувати перегляд сторінки клієнта шляхом вилучення з бази даних для цього клієнта за ідентифікатором клієнта, який ви отримали після успішної вставки в базу даних.
Анжелін Надар

Але я згоден з рішенням @Eugen Konkov
Angelin Nadar

10

Безпосередньо, ви не можете, і це добре. Попередження браузера існує з причини. Ця тема повинна відповісти на ваше запитання:

Запобігати показу кнопці "Назад" показувати попередження про підтвердження POST

Запропоновано два ключових способи вирішення: модель PRG та подання AJAX з подальшим перенесенням сценарію.

Зауважте, що якщо ваш метод дозволяє отримати GET, а не метод подання POST, це дозволить вирішити проблему і краще відповідатиме умові. Ці рішення надаються на основі припущення, що ви хочете / потребуєте POST даних.


8

Єдиний спосіб бути впевненим в тому, що одна і та ж форма ніколи не надсилається двічі, - це вставити унікальний ідентифікатор у кожен з них, який ви видаєте, і відстежити, які з них були подані на сервері. Проблема полягає в тому, що якщо користувач створює резервну копію сторінки, де була форма, і вводить нові дані, та сама форма не працюватиме.


6

Є дві частини відповіді:

  1. Переконайтесь, що дублікати публікацій не псуються з вашими даними на стороні сервера. Для цього вставте в публікацію унікальний ідентифікатор, щоб ви могли відхиляти наступні запити на стороні сервера. Ця схема називається Idempotent Receiver в умовах обміну повідомленнями.

  2. Переконайтеся, що користувача не турбує можливість дублювання подань обох

    • переадресація на GET після POST (POST перенаправлення GET pattern)
    • відключення кнопки за допомогою javascript

Ніщо, що ви робите у віці до 2 років, повністю не завадить повторювати подання. Люди можуть натискати дуже швидко, і хакери можуть публікувати публікації в будь-якому випадку. Вам завжди потрібен 1. якщо ви хочете бути абсолютно впевнені, що немає дублікатів.


2

Якщо ви оновите сторінку з даними POST, браузер підтвердить вашу повторну подачу. Якщо ви використовуєте дані GET, повідомлення не відображатиметься. Ви також можете мати другу сторінку, збереживши подання, перенаправити на третю сторінку без даних.


2

Ну, я знайшов, що ніхто не згадав про цю хитрість.

Без перенаправлення ви все одно можете запобігти підтвердженню форми під час оновлення.

За замовчуванням код форми такий:

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

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

Ви побачите чари.

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


2

Це можна зробити за допомогою jquery

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

Це найелегантніший спосіб запобігти повторному надходженню даних після надсилання повідомлення.

Сподіваюся, це допомагає.


0

Шаблон PRG може запобігти лише повторне надсилання, викликане оновленням сторінки. Це не 100% безпечний захід.

Зазвичай я буду вживати заходів нижче, щоб запобігти повторному надсиланню:

  1. Клієнтська сторона - Використовуйте javascript для запобігання повторного натискання кнопки, яка спричинить подання форми. Ви можете просто відключити кнопку після першого клацання.

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

У більшості випадків ці заходи можуть допомогти запобігти повторній подачі.


0

Мені дуже подобається відповідь @ Ангеліна. Але якщо ви маєте справу зі старим кодом, де це не практично, ця методика може працювати для вас.

У верхній частині файлу

// 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/


0

використовувати js для запобігання додавання даних:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.