Как предотвратить повторную отправку формы при обновлении страницы (F5 / CTRL+R)



У меня есть простая форма, которая отправляет текст в моей таблице SQL. Проблема в том, что после того, как пользователь отправляет текст, они могут обновить страницу, и данные будут отправлены снова без повторного заполнения формы. Я мог бы перенаправить пользователя на другую страницу после отправки текста, но я хочу, чтобы пользователи оставались на той же странице.



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

3779   18  

18 ответов:

используйте шаблон Post/Redirect/Get. http://en.wikipedia.org/wiki/Post/Redirect/Get

с помощью моего веб-сайта я сохраню сообщение в файле cookie или сеансе, перенаправлю после публикации, прочитаю файл cookie/сеанс, а затем очистите значение этой переменной сеанса или файла cookie.

Я также хотел бы отметить, что вы можете использовать подход javascript,window.history.replaceState чтобы предотвратить повторную отправку на кнопку Обновить и назад.

<script>
    if ( window.history.replaceState ) {
        window.history.replaceState( null, null, window.location.href );
    }
</script>

доказательство концепции здесь:https://dtbaker.net/files/prevent-post-resubmit.php

Я бы все равно рекомендовал подход Post/Redirect/Get, но это новое решение JS.

вы действительно должны использовать шаблон Post Redirect Get для обработки этого, но если вы каким-то образом оказались в положении, когда PRG нежизнеспособен (например, сама форма находится в include, предотвращая перенаправления), вы можете хэшировать некоторые параметры запроса, чтобы сделать строку на основе содержимого, а затем проверить, что вы ее еще не отправили.

//create digest of the form submission:

    $messageIdent = md5($_POST['name'] . $_POST['email'] . $_POST['phone'] . $_POST['comment']);

//and check it against the stored value:

    $sessionMessageIdent = isset($_SESSION['messageIdent'])?$_SESSION['messageIdent']:'';

    if($messageIdent!=$sessionMessageIdent){//if its different:          
        //save the session var:
            $_SESSION['messageIdent'] = $messageIdent;
        //and...
            do_your_thang();
    } else {
        //you've sent this already!
    }

вы можете повторно отправить форму через переменную сеанса, например

сначала вы должны установить rand () в текстовом поле и $_SESSION['rand'] на странице формы

<form action="" method="post">
  <?php
   $rand=rand();
   $_SESSION['rand']=$rand;
  ?>
 <input type="hidden" value="<?php echo $rand; ?>" name="randcheck" />
   Your Form's Other Field 
 <input type="submit" name="submitbtn" value="submit" />
</form>

после проверки $_SESSION ['rand' ] с textbox $_POST ['randcheck'] значение Как

         if(isset($_POST['submitbtn']) && $_POST['randcheck']==$_SESSION['rand'])
          {
         //Your Code here
         }
  1. использовать заголовок и перенаправить страницу.

    header("Location:your_page.php"); вы можете перенаправить на ту же страницу или другую страницу.

  2. Unset $_POST после вставки его в базу данных.

    unset($_POST);

когда форма обработана, вы перенаправляете на другую страницу:

... process complete....
header('Location: thankyou.php');

вы также можете перенаправить на ту же страницу.

Если вы делаете что-то вроде комментариев и вы хотите, чтобы пользователь оставался на той же странице, вы можете использовать AJAX для обработки формы представления

довольно верный способ реализовать уникальный идентификатор в пост и кэшировать его в

<input type='hidden' name='post_id' value='".createPassword(64)."'>

тогда в вашем коде сделайте следующее:

if( ($_SESSION['post_id'] != $_POST['post_id']) )
{
    $_SESSION['post_id'] = $_POST['post_id'];
    //do post stuff
} else {
    //normal display
}

function createPassword($length)
{
    $chars = "abcdefghijkmnopqrstuvwxyz023456789";
    srand((double)microtime()*1000000);
    $i = 0;
    $pass = '' ;

    while ($i <= ($length - 1)) {
        $num = rand() % 33;
        $tmp = substr($chars, $num, 1);
        $pass = $pass . $tmp;
        $i++;
    }
    return $pass;
}

я нашел следующее решение. Вы можете избежать перенаправления после обработки POST запрос манипулируя history такой:

$cat process.php
<?php 
     process POST data here
     ... 
     header('Location: /the/result/page');
     exit();
?>

enter image description here

после обработки POSTданные ЭД вы делаете небольшой <script> и результат /the/result/page

<?php 
     process POST data here
     render the <script>         // see below
     render `/the/result/page`   // OK
?>

The <script> вы должны предоставить:

<script>
    window.onload = function() {
        history.replaceState("", "", "/the/result/page");
    }
</script>

результат:

enter image description here

как вы можете видеть данные формы POSTЭд process.php сценарий.
Этот скрипт процесс POSTЭд данные и рендеринг /the/result/page сразу с:

  1. нет перенаправления
  2. нет rePOST данные при обновлении страницы (F5)
  3. нет rePOST при переходе на предыдущую/следующую страницу через историю браузера

UPD

в качестве другого решения я прошу запрос the Mozilla FireFox команда, чтобы позволить пользователям устанавливать NextPage заголовок, который будет работать как и сделать post/redirect/get шаблон устарел.

короче. Когда серверная форма процесса POST данные успешно это:

  1. Setup NextPage заголовок вместо Location
  2. отображать результат обработки POST форма данных, как это будет отображаться для GET запрос post/redirect/get pattern

браузер, в свою очередь, когда вижу NextPage заголовок:

  1. настройки window.location С NextPage стоимостью
  2. когда пользователь обновит страницу, браузер будет вести переговоры GET запрос NextPage вместо rePOST данные формы

я думаю, что это было бы превосходно, если реализовали бы, не так ли? =)

просто перенаправьте его на ту же страницу после использования данных формы, и он работает. Я уже пробовал.

header('location:yourpage.php');

уточненная версия сообщения Moob. Создайте хэш сообщения, сохраните его как файл cookie сеанса и сравните хэши каждого сеанса.

// Optionally Disable browser caching on "Back"
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
header( 'Expires: Sun, 1 Jan 2000 12:00:00 GMT' );
header( 'Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT' );

$post_hash = md5( json_encode( $_POST ) );

if( session_start() )
{
    $post_resubmitted = isset( $_SESSION[ 'post_hash' ] ) && $_SESSION[ 'post_hash' ] == $post_hash;
    $_SESSION[ 'post_hash' ] = $post_hash;
    session_write_close();
}
else
{
    $post_resubmitted = false;
}

if ( $post_resubmitted ) {
  // POST was resubmitted
}
else
{
  // POST was submitted normally
}

после вставки его в базу данных, вызовите метод unset () для очистки данных.

unset ($_POST);

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

заголовок ('Location:'.$_SERVER ['PHP_SELF']);

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

пример базового сценария :

нажмите на кнопку Отправить дважды

способ решить

  • на стороне клиента

    • отключить кнопку отправки, как только клиент нажмет на нее
    • если вы используете Jquery:Jquery.один
    • PRG Шаблон
  • сервер

    • использование дифференцировать на основе хэширования timestamp / timestamp, когда запрос был отправлен.
    • токены Userequest. Когда основная загрузка назначает временный запрос tocken, который при повторении игнорируется.

Как предотвратить повторную отправку формы php без перенаправления. Если вы используете $_SESSION (after session_start) и форму $_POST, вы можете сделать что-то вроде этого:

if ( !empty($_SESSION['act']) && !empty($_POST['act']) && $_POST['act'] == $_SESSION['act'] ) {
  // do your stuff, save data into database, etc
}

в вашей html форме поставьте это:

<input type="hidden" id="act" name="act" value="<?php echo ( empty($_POST['act']) || $_POST['act']==2 )? 1 : 2; ?>">
<?php
if ( $_POST['act'] == $_SESSION['act'] ){
    if ( empty( $_SESSION['act'] ) || $_SESSION['act'] == 2 ){
        $_SESSION['act'] = 1;
    } else {
        $_SESSION['act'] = 2;
    }
}
?>

Ps: Если вы используете форму Get, вы можете легко изменить все сообщения с GET, и это тоже работает.

Я использую эту строку javascript, чтобы заблокировать всплывающее окно с запросом на повторную отправку формы при обновлении после отправки формы.

if ( window.history.replaceState ) {
  window.history.replaceState( null, null, window.location.href );
}

просто поместите эту строку в нижний колонтитул вашего файла и увидеть магию

Использование шаблона Post / Redirect / Get из ответа Keverw-хорошая идея. Однако вы не можете оставаться на своей странице (и я думаю, что это было то, что вы просили?) Кроме того, это может иногда fail:

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

другой вариант будет хранить в сеансе, если текст должен быть записан в вашу базу данных SQL следующим образом:

if($_SERVER['REQUEST_METHOD'] != 'POST')
{
  $_SESSION['writeSQL'] = true;
}
else
{
  if(isset($_SESSION['writeSQL']) && $_SESSION['writeSQL'])
  {
    $_SESSION['writeSQL'] = false;

    /* save $_POST values into SQL */
  }
}

Как говорили другие, невозможно выйти из использования post/redirect/get. Но в то же время это достаточно легко сделать, что вы хотите сделать на стороне сервера.

на странице POST вы просто проверяете пользовательский ввод, но не действуете на нем, вместо этого вы копируете его в массив сеанса. Затем вы снова перенаправляетесь на главную страницу отправки. Ваша главная страница отправки начинается с проверки наличия массива сеансов, который вы используете, и если да, то скопируйте его в локальный массив и снимите его. Оттуда вы можете действовать на него.

таким образом, вы только делаете всю свою основную работу один раз, добиваясь того, что вы хотите сделать.

Я искал решение, чтобы предотвратить повторную отправку в огромный проект после этого. Код очень хорошо работает с $_GET и $_POST, и я не могу изменить поведение элементов формы без риска непредвиденных ошибок. Итак, вот мой код:

<!-- language: lang-php -->
<?php

// Very top of your code:

// Start session:
session_start();

// If Post Form Data send and no File Upload
if ( empty( $_FILES ) && ! empty( $_POST ) ) {
    // Store Post Form Data in Session Variable
    $_SESSION["POST"] = $_POST;
    // Reload Page if there were no outputs
    if ( ! headers_sent() ) {
        // Build URL to reload with GET Parameters
        // Change https to http if your site has no ssl
        $location = "https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        // Reload Page
        header( "location: " . $location, true, 303 );
        // Stop any further progress
        die();
    }
}

// Rebuilt POST Form Data from Session Variable
if ( isset( $_SESSION["POST"] ) ) {
    $_POST = $_SESSION["POST"];
    // Tell PHP that POST is sent
    $_SERVER['REQUEST_METHOD'] = 'POST';
}

// Your code:
?><html>
    <head>
        <title>GET/POST Resubmit</title>
    </head>
    <body>

    <h1>Forms:</h1>
    <h2>GET Form:</h2>
    <form action="index.php" method="get">
        <input type="text" id="text_get" value="test text get" name="text_get"/>
        <input type="submit" value="submit">
    </form>
    <h2>POST Form:</h2>
    <form action="index.php" method="post">
        <input type="text" id="text_post" value="test text post" name="text_post"/>
        <input type="submit" value="submit">
    </form>
    <h2>POST Form with GET action:</h2>
    <form action="index.php?text_get2=getwithpost" method="post">
        <input type="text" id="text_post2" value="test text get post" name="text_post2"/>
        <input type="submit" value="submit">
    </form>
    <h2>File Upload Form:</h2>
    <form action="index.php" method="post" enctype="multipart/form-data">
        <input type="file" id="file" name="file">
        <input type="submit" value="submit">
    </form>

    <h1>Results:</h1>
    <h2>GET Form Result:</h2>
    <p>text_get: <?php echo $_GET["text_get"]; ?></p>
    <h2>POST Form Result:</h2>
    <p>text_post: <?php echo $_POST["text_post"]; ?></p>
    <h2>POST Form with GET Result:</h2>
    <p>text_get2: <?php echo $_GET["text_get2"]; ?></p>
    <p>text_post2: <?php echo $_POST["text_post2"]; ?></p>
    <h2>File Upload:</h2>
    <p>file:
    <pre><?php if ( ! empty( $_FILES ) ) {
            echo print_r( $_FILES, true );
        } ?></pre>
    </p>
    <p></p>
    </body>
    </html><?php
// Very Bottom of your code:
// Kill Post Form Data Session Variable, so User can reload the Page without sending post data twice
unset( $_SESSION["POST"] );

Он работает только для того, чтобы избежать повторной отправки $_POST, а не $_GET. Но это то поведение, которое мне нужно. Проблема повторной отправки не работает с загрузкой файлов!

почему бы просто не использовать $_POST['submit'] переменная как логическое утверждение, чтобы сохранить все, что находится в форме. Вы всегда можете перенаправить на ту же страницу (в случае их обновления, и когда они нажмите go back в браузере переменная submit post больше не будет установлена. Просто убедитесь, что ваша кнопка отправки имеет name и id на submit.

Comments

    Ничего не найдено.