ページが更新されたときにフォームの再送信を防ぐ方法(F5 / CTRL + R)


132

SQLテーブルにテキストを送信する単純なフォームがあります。問題は、ユーザーがテキストを送信した後、ページを更新でき、フォームに再度入力しなくてもデータが再度送信されることです。テキストが送信された後、ユーザーを別のページにリダイレクトすることはできますが、ユーザーに同じページを表示させ続けたいです。

各ユーザーに一意のセッションIDを与え、それを別の値と比較して、私が抱えている問題を解決したことについて何か読んだのを覚えていますが、どこにあるのか忘れてしまいました。



1
なぜユーザーを別のページにリダイレクトしたくないのですか?
アダム

@Adam:これはサーバーに別の要求を行うには過剰であり、サーバーはDBからデータを再度フェッチします。処理中に、我々はすでに必要なすべてのデータをフェッチするので、しかし、これは資源の浪費であるPOST要求を
オイゲン・Konkov

PRGパターンの@EugenKonkovでは、成功メッセージを表示するページにリダイレクトするだけです。DBからさらにフェッチする必要はありません。
アダム

@Adam:POSTデータを入力して作成されたレコード全体を表示することもできます。この場合SELECT、DB からそれを行う必要があります。たとえば、請求書を作成すると、/invoices/53「成功」ではなく請求書全体を表示するようにリダイレクトされます
Eugen Konkov

回答:


96

Post / Redirect / Getパターンを使用します。http://en.wikipedia.org/wiki/Post/Redirect/Get

私のWebサイトでは、メッセージをCookieまたはセッションに保存し、投稿後にリダイレクトし、Cookie /セッションを読み取り、そのセッションまたはCookie変数の値をクリアします。


1
これにより、Chromeはビジネスが投稿のみを必要とする開発に使用できなくなります。
John Peters

26
PRGパターンを使用する場合、実際にページを離れますか?リダイレクトしないときにどのように機能させるかという問題ではありませんか?
アダム

1
同じページにリダイレクトすると、$ _ POSTがクリアされるようです。私が理解しているように、それは望ましい効果でした。とにかく、それは私の望んだ効果でした。でも、それを明示的にすればもっといい答えになると思います。
donutguy640

うまく機能します。私の唯一の推奨事項は、厳密なエラーテストを有効にして、開発中にローカルでエラーをキャッチすることを確認することです。
モーリス

質問には答えません。なぜそれが受け入れられた答えなのかわからない。
YungGun

124

また、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ソリューションです。


1
おかげで、@ dtbaker、それは素晴らしいです:)
Hasan

1
TKSは、これは私のための作業に最適です、それだけで回避ユーザーmissunderstanding 404ページを作成する必要がありました
テスHSU

ただ素晴らしい。ありがとう
user4906240

2
サファリでは機能しませんが、hrefは変更されましたが、送信リクエストを送信してデータを保持しています
Vo Thanh Tung

これは私のために働きましたありがとう!なぜPRGアプローチを推奨するのですか?
iJassar

16

これを処理するには、実際には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!
    }

共有していただきありがとうございます。これではうまくいかないようです。ページを更新すると、実際にフォームが再送信されます。おそらく何かが足りないのでしょうか?
aLearner

ページを更新するとすべてが再送信されますが、前回送信されたデータ(セッションvar 'messageIdent'に保存されているデータ)と異なる場合のみ、送信データの処理を選択します。'if messageIdents are different'句(つまり、 'do_your_thang()'がどこにあるか)内でフォーム送信を処理していますか?
Moob 2013年

お返事をありがとうございます。結局、Post / Redirect / Getパターンを実装しただけだったので、何が原因なのか今は覚えていません。戻ってきてくれてありがとう。
aLearner 2013年

この方法は、再送信をブロックすることを意図したものではありません...再送信を検出することを意図しているため、これが新しい送信であった場合に何をしても「実行」しないようにコードを変更できます。つまり、この方法を使用すると、「元の」送信を検出してデータを配置できます。オリジナルでない場合は、データを配置しないでください。「重複の試み」の警告を表示するか、代わりに「確認」を表示します。
TheSatinKnight 2015

これを機能させるsession_start();には、ファイルの先頭に追加してセッションを開始している必要があります。w3schools.com/php/php_sessions.aspは言う注:にsession_start()関数は、ドキュメント内の非常に最初のものでなければなりません。HTMLタグの前。
wkille

16

このJavaScript行を使用して、フォームが送信されたときに更新時にフォームの再送信を求めるポップアップをブロックします。

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

この行をファイルのフッターに置くだけで、魔法を見ることができます


クライアント側から無効にできないこのバージョンはいいでしょうが、それは短く、簡単で、速く...そして必要なことを行います。ユーザーが投稿を再送信せずに戻ることができるように、履歴を保持します。
ペーテルVértényi

16

セッション変数を使用してフォームの再送信を防ぐことができます。

まず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
}

3
<input type="hidden" name="randcheck" id="randcheck" value="<?php echo microtime(); ?>" />代わりに使用できます
Nikolay Bronskiy

はい、rand()の代わりにmicrotime()とtime()を使用できます。これは、使用できるさまざまな値を提供する関数や変数です。ただし、その値をSESSION変数に設定していることを確認してください。ここで、SESSIONはrandcheckフィールドでチェックし、フォームを再送信しないようにする必要があります。
Savoo 2018年

セッション変数とポスト変数が一致するかどうかを確認した後、セッション変数をクリアしないでください。
ノイズ除去

1
@denoise @Savooの考え$_POSTは、フォームデータの後、同じページがリロードしてセッションとポスト変数を再設定することです。また、変数の作成は、変数が一致するかどうかを確認した後に行う必要があります。したがってunset必要ありません。
HalfMens

13

フォームが処理されたら、別のページにリダイレクトします。

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

同じページにリダイレクトすることもできます。

コメントのようなことをしていて、ユーザーが同じページにとどまるようにしたい場合は、Ajaxを使用してフォームの送信を処理できます。


12

次の回避策を見つけました。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();
?>

ここに画像の説明を入力してください

POSTedデータを処理した後、小さくレンダリング<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レンダリングするのと同じように、フォームデータの処理結果をレンダリングGETpost/redirect/getます。

NextPageヘッダーが表示されたブラウザー:

  1. 調整window.locationNextPage
  2. ユーザーがページを更新すると、ブラウザはデータを再形成GETするNextPage代わりにリクエストをネゴシエートしPOSTます

実装すればこれは優れていると思いませんか? =)


11
  1. ヘッダーを使用してページをリダイレクトします。

    header("Location:your_page.php"); 同じページまたは別のページにリダイレクトできます。

  2. データベースに挿入した後、$ _ POSTの設定を解除します。

    unset($_POST);


54
$ _POSTの設定を解除しても、フォームの再送信にはまったく影響しません。少なくともChromeでは影響はありません。
Gavin

2
うまくいかないだろう。ユーザーがバックページに移動すると、POST変数の設定をもう一度実行します。
Sandhu

使用する理由は何unsetですか?他の人がそれから学ぶことができるように、あなたの答えにいくつかの説明を加えてください
ニコ・ハーセ

7

かなり確実な方法は、一意のIDを投稿に実装し、それを

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

私は実際にこれに似たものを書いただけですが、パスワードを作成する手間をかけずに、フォームを列挙しただけです(例:ステップ1〜5)。したがって、それらが等しい場合は、先に進んでください。それ以外の場合は、dbに保存したり、電子メールを送信したりしないでください。しかし、ユーザーがどこへ行っても着陸できるようにします。
フェルナンドシルバ

1
私はこれがページをリロードするよりも良い解決策だと思います。投稿が成功したときにメッセージを表示し、ページをリロードするとメッセージが削除されるためです。これは私にとってすりおろしです。uniqidを使用することもできます
JulioPopócatl15年

6

この方法は私にとってうまくいき、私はこれがこの仕事をする最も簡単な方法だと思います。

一般的な考え方は、フォームの送信後にユーザーを他のページにリダイレクトすることです。これにより、ページの更新時にフォームの再送信が停止します。それでも、フォームの送信後にユーザーを同じページに固定する必要がある場合は、複数の方法で行うことができますが、ここではJavaScriptメソッドについて説明します。

JavaScriptメソッド

この方法は非常に簡単で、フォームが送信されると、更新時にフォームの再送信を求めるポップアップをブロックします。このJavaScriptコードの行をファイルのフッターに配置するだけで、マジックが表示されます。

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


これは私にも役立ちます
Ankit

4

JavaScript

この方法は非常に簡単で、フォームが送信されると、更新時にフォームの再送信を求めるポップアップをブロックします。このJavaScriptコードの行をファイルのフッターに配置するだけで、マジックが表示されます。

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

3

フォームデータを使用した後、同じページにリダイレクトするだけで機能します。私はそれを試しました。

header('location:yourpage.php');

4
これは、誰かが数年前に与えたのと同じ答えを繰り返すだけです。(実際には他に2つ!)
Nick Rice 14

他の人の回答を複製する場合は、それを面白くするために、少なくともいくつかの説明に回答を追加する必要があります
Nico Haase

3

Moobの投稿の洗練されたバージョン。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
}

2

基本的には、そのページからリダイレクトする必要がありますが、インターネットが遅いときにも問題が発生する可能性があります(サーバーサイドからのリダイレクトヘッダー)

基本的なシナリオの例:

送信ボタンを2回クリックします

解決する方法

  • クライアント側

    • クライアントがクリックしたら送信ボタンを無効にする
    • Jqueryを使用している場合:Jquery.one
    • PRGパターン
  • サーバ側

    • リクエストに基づいたハッシュタイムスタンプ/タイムスタンプの区別。
    • リクエストトークンを使用します。メインがロードされたら、一時的なリクエストトークンを割り当てます。これは繰り返されても無視されます。

2

リダイレクトせずに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フォームを使用している場合、GETを使用してすべてのPOSTを簡単に変更でき、それも機能します。


1

データベースに挿入した後、unset()メソッドを呼び出してデータをクリアします。

unset($ _ POST);

更新データの挿入を防ぐには、レコード挿入後に同じページまたは別のページにページリダイレクトを実行します。

header( 'Location:'。$ _ SERVER ['PHP_SELF']);


1
他の人がそれから学ぶことができるように、回答にいくつかの説明を追加してください-なぜそのunset電話が必要なのですか?スキップするとどうなりますか?
ニコHaase

0

Keverwの回答からPost / Redirect / Getパターンを使用することをお勧めします。しかし、あなたはあなたのページに留まることができません(そして私はこれがあなたが求めていたものだったと思いますか?)さらに、それは時々失敗するかもしれません

サーバーの遅延のために最初の送信が完了する前にWebユーザーが更新され、特定のユーザーエージェントで重複した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 */
  }
}

0

他の人が言ったように、post / redirect / getを使用することはできません。しかし同時に、サーバーサイドでやりたいことを簡単に行うことができます。

POSTページでは、ユーザー入力を検証するだけで動作しません。代わりに、それをSESSION配列にコピーします。その後、メインの送信ページに再度リダイレクトします。メインの送信ページは、使用しているSESSION配列が存在するかどうかを確認することから始まり、存在する場合はローカル配列にコピーして設定を解除します。そこから行動することができます。

このようにして、すべての主要な作業を一度だけ実行して、やりたいことを達成します。


0

その後、巨大プロジェクトでの再提出を防ぐための解決策を探しました。コードは$ _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"] );

$ _GETではなく、$ _ POSTの再送信を回避するためにのみ機能します。しかし、これは私が必要とする行動です。再送信の問題はファイルのアップロードでは機能しません!


0

私にとって有効なのは:

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>

0

このform.phpサンプルは、PRGの正しい使用方法を示しています(フォームが有効かどうか)。

  • フォームが有効でアクションが実行された場合にのみ、同じページにリダイレクトします。
  • リダイレクトは、ページの更新時にフォームが再送信されないように保護します。
  • セッションを使用して、フォームが有効なときに表示する成功メッセージを失わないようにします。
  • テストには、「有効な送信」、「無効な送信」の2つのボタンがあります。両方を試してから、ページを更新してください。
<?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>

-2
if (($_SERVER['REQUEST_METHOD'] == 'POST') and (isset($_SESSION['uniq']))){
    if($everything_fine){
        unset($_SESSION['uniq']);
    }
}
else{
    $_SESSION['uniq'] = uniqid();
}

他の人がそれから学ぶことができるように、あなたの答えにいくつかの説明を追加してください-どこ$everything_fineから来ましたか?
ニコHaase

-3

$_POST['submit']フォームの内容をすべて保存するために、変数を論理ステートメントとして使用しないでください。あなたは、常に彼らはリフレッシュの場合には、同じページ(にリダイレクトすることができ、それらは打ったときgo backのブラウザで、ポスト変数はもう設定されていないだろう提出。ちょうどあなたのsubmitボタンが持っていることを確認してくださいnameidsubmit


1
見てくださいen.wikipedia.org/wiki/Post/Redirect/Getを!これは正しいアプローチです
jan267
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.