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

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

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

24 ответа

Решение

Используйте шаблон 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.

Вы можете предотвратить повторную отправку формы через переменную сессии.

сначала вы должны установить 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'] со значением текстового поля $_POST['randcheck']

         if(isset($_POST['submitbtn']) && $_POST['randcheck']==$_SESSION['rand'])
          {
         //Your Code here
         }

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

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

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

Этот метод мне подходит, и я думаю, что это самый простой способ выполнить эту работу.

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

Метод Javascript

Этот метод довольно прост и блокирует всплывающее окно с запросом на повторную отправку формы при обновлении после отправки формы. Просто поместите эту строку кода javascript в нижний колонтитул файла и увидите волшебство.

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

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

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

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

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

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

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

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

Итак, у вас есть HTML-форма:

<form method=POST action='/process.php'>
 <input type=submit value=OK>
</form>

Когда вы обрабатываете эту форму на своем сервере, вы вместо перенаправления пользователя на /the/result/page путем настройки Location заголовок как это:

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

После обработки POST Редкие данные, которые вы отображаете <script> и результат /the/result/page

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

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

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

Результат:

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

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

UPD

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

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

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

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

  1. регулировать window.location с NextPage значение
  2. Когда пользователь обновит страницу, браузер проведет переговоры GET запросить NextPage вместо POST данные формы

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

  1. Используйте заголовок и перенаправьте страницу.

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

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

    unset($_POST);

Javascript

Этот метод довольно прост и блокирует всплывающее окно с запросом на повторную отправку формы при обновлении после отправки формы. Просто поместите эту строку кода javascript в нижний колонтитул файла и увидите волшебство.

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

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

<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;
}

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

header('location:yourpage.php');

Уточненная версия поста Муба. Создайте хеш POST, сохраните его как файл 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(), чтобы очистить данные.

снята с охраны ($_POST);

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

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

Как предотвратить повторную отправку php формы без перенаправления. Если вы используете $_SESSION (после 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, вы можете легко изменить все POST с помощью GET, и это тоже работает.

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

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

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

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

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

    • Отключить кнопку отправки после того, как клиент нажмет
    • Если вы используете Jquery: Jquery.one
    • PRG Pattern
  • Серверная сторона

    • Использование дифференцированной временной метки / метки времени при отправке запроса.
    • Используйте по запросу жетоны. При загрузке основного устройства назначьте временный запрос токена, который при повторении игнорируется.

Переменная $_POST['submit'] не будет существовать при начальной загрузке страницы, и curl может быть запущен, только если условие ниже истинно.

      if($_POST['submit'] == "submit"){

// This is where you run the Curl code and display the output
  $curl = curl_init();



//clear $post variables after posting
$_POST = array();

}

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

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

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

Что мне подходит:

if ( !refreshed()) {
   //Your Submit Here
        if (isset( $_GET['refresh'])) {
            setcookie("refresh",$_GET['refresh'], time() + (86400 * 5), "/");
        }

    }    
}


function refreshed()
{
    if (isset($_GET['refresh'])) {
        $token = $_GET['refresh'];
        if (isset($_COOKIE['refresh'])) {
            if ($_COOKIE['refresh'] != $token) {
                return false;
            } else {
                return true;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}  


function createToken($length) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

?>

И в твоей форме

 <form  action="?refresh=<?php echo createToken(3)?>">



 </form>

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

Если веб-пользователь обновляется до завершения первоначальной отправки из-за задержки на сервере, что приводит к дублированию запроса 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 */
  }
}

Я искал решение, чтобы предотвратить повторную передачу в большом проекте впоследствии. Код отлично работает с $_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. Но это поведение, которое мне нужно. Проблема повторной отправки не работает с загрузкой файлов!

Эта form.php пример показывает, как правильно использовать PRG (когда форма действительна или нет).

  • Он перенаправляет на ту же страницу, только когда форма действительна и действие было выполнено.
  • Перенаправление защищает форму от повторной отправки при обновлении страницы.
  • Он использует сеанс, чтобы не терять сообщения об успехе, которые вы хотите показать, когда форма действительна.
  • Есть две кнопки для тестирования: "Допустимая отправка", "Недействительная отправка". Попробуйте оба варианта и после этого обновите страницу.
<?php
session_start();

function doSelfRedirect()
{
  header('Location:'.$_SERVER['PHP_SELF']);
  exit;
}

function setFlashMessage($msg)
{
  $_SESSION['message'] = $msg;
}

function getFlashMessage()
{
  if (!empty($_SESSION['message'])) {
    $msg = $_SESSION['message'];
    unset($_SESSION['message']);
  } else {
    $msg = null;
  }

  return $msg;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  // Validation primitive example.
  if (empty($_POST['valid'])) {
    $formIsValid = false;
    setFlashMessage('Invalid form submit');
  } else {
    $formIsValid = true;
  }

  if ($formIsValid) {
    // Perform any actions here.
    // ...

    // Cool!
    setFlashMessage('Form is valid. Action performed.');

    // Prevent form resubmission.
    doSelfRedirect();
  }
}
?>
<h1>Hello form</h1>

<?php if ($msg = getFlashMessage()): ?>
  <div><?= $msg ?></div>
<?php endif; ?>

<form method="post">
  <input type="text" name="foo" value="bar"><br><br>
  <button type="submit" name="invalid" value="0">Invalid submit</button>
  <button type="submit" name="valid" value="1">Valid submit</button>
</form>
if (($_SERVER['REQUEST_METHOD'] == 'POST') and (isset($_SESSION['uniq']))){
    if($everything_fine){
        unset($_SESSION['uniq']);
    }
}
else{
    $_SESSION['uniq'] = uniqid();
}

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

Другие вопросы по тегам