web-dev-qa-db-fra.com

Comment empêcher la nouvelle soumission du formulaire lorsque la page est actualisée (F5/CTRL + R)

J'ai un formulaire simple qui envoie du texte à ma table SQL. Le problème est qu'après que l'utilisateur a soumis le texte, il peut actualiser la page et les données sont soumises à nouveau sans remplir à nouveau le formulaire. Je pourrais rediriger l'utilisateur vers une autre page une fois le texte soumis, mais je souhaite que les utilisateurs restent sur la même page.

Je me souviens avoir lu quelque chose sur le fait de donner à chaque utilisateur un identifiant de session unique et de le comparer avec une autre valeur qui résolvait le problème que j’avais mais j’ai oublié où il se trouve.

94
user701510

Utilisez le modèle Post/Redirect/Get. http://en.wikipedia.org/wiki/Post/Redirect/Get

Avec mon site Web, je vais stocker un message dans un cookie ou une session, rediriger après le message, lire le cookie/la session, puis effacer la valeur de cette variable de session ou de cookie.

72
Keverw

Je tiens également à souligner que vous pouvez utiliser une approche javascript, window.history.replaceState, pour empêcher une nouvelle soumission lors de l'actualisation et du bouton Précédent.

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

Preuve de concept ici: https://dtbaker.net/files/prevent-post-resubmit.php

Je recommanderais toujours une approche Post/Redirect/Get, mais il s'agit d'une nouvelle solution JS.

38
dtbaker

Vous devriez vraiment utiliser un motif «Get Redirect Get» pour gérer cela, mais si vous vous retrouvez dans une position où PRG n'est pas viable (par exemple, le formulaire est lui-même dans une inclusion, ce qui empêche les redirections), vous pouvez hacher certains paramètres de requête. créer une chaîne en fonction du contenu, puis vérifiez que vous ne l’avez pas déjà envoyée.

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

vous pouvez effectuer une nouvelle soumission de formulaire via une variable de session, comme

vous devez d’abord définir Rand () dans la zone de texte et $ _SESSION ['Rand'] sur la page du formulaire

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

Après vérification, $ _SESSION ['Rand'] avec la zone de texte $ _POST ['randcheck'] valueLike

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

Lorsque le formulaire est traité, vous redirigez vers une autre page:

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

vous pouvez également rediriger vers la même page.

si vous faites quelque chose comme des commentaires et que vous voulez que l'utilisateur reste sur la même page, vous pouvez utiliser Ajax pour gérer l'envoi du formulaire

12
Ibu
  1. Utilisez l'en-tête et redirigez la page.

    header("Location:your_page.php"); Vous pouvez rediriger vers une même page ou une autre page.

  2. Désactivez $ _POST après l'avoir inséré dans la base de données.

    unset($_POST);

11
Prasanth Bendra

J'utilise cette ligne javascript pour bloquer la fenêtre contextuelle demandant de soumettre à nouveau le formulaire lors de l'actualisation une fois le formulaire soumis. 

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

Il suffit de placer cette ligne au pied de votre fichier et de voir la magie

8
Mo'men Mohamed

J'ai trouvé la solution suivante. Vous pouvez échapper à la redirection après avoir traité la demande POST en manipulant history object. 

Donc vous avez le formulaire HTML:

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

Lorsque vous traitez ce formulaire sur votre serveur, au lieu de rediriger l'utilisateur vers /the/result/page, configurez l'en-tête Location comme ceci:

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

 enter image description here

Après le traitement des données POSTed, vous rendez le petit <script> et le résultat /the/result/page.

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

Le <script> à rendre:

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

Le résultat est:

 enter image description here

comme vous pouvez le constater, les données du formulaire sont POSTed to process.php script.
Ce script processus POSTed données et rendu /the/result/page immédiatement avec:

  1. pas de redirection
  2. pas de données rePOST lorsque vous actualisez la page (F5) 
  3. no rePOST lorsque vous naviguez vers la page précédente/suivante dans l'historique du navigateur

UPD

Comme autre solution, je demande à demande de fonctionnalité l’équipe Mozilla FireFox de permettre aux utilisateurs de configurer l’en-tête NextPage qui fonctionnera comme l’en-tête Location et de rendre le modèle post/redirect/get obsolète.

En bref. Lorsque le serveur traite les données POST avec succès, il:

  1. Configurer l'en-tête NextPage au lieu de Location
  2. Rendre le résultat du traitement des données de formulaire POST comme il le ferait pour la demande GET dans le modèle post/redirect/get

Le navigateur, à son tour, voit l’en-tête NextPage:

  1. Ajustez window.location avec la valeur NextPage
  2. Lorsque l'utilisateur actualise la page, le navigateur négocie la demande GET en NextPage au lieu des données de formulaire rePOST.

Je pense que ce serait excellent s'il était mis en œuvre, non? =)

7
Eugen Konkov

Une méthode assez sûre consiste à implémenter un identifiant unique dans la publication et à 

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

Ensuite, dans votre code, faites ceci:

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

Il vous suffit de le rediriger vers la même page après avoir utilisé les données de formulaire, et cela fonctionne. J'ai essayé.

header('location:yourpage.php');
4
krsoni

Une version raffinée du post de Moob. Créez un hachage du POST, enregistrez-le en tant que cookie de session et comparez les hachages à chaque session.

// 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
}
3
skibulk

Après l'avoir insérée dans la base de données, appelez la méthode unset () pour effacer les données. 

non défini ($ _ POST);

Pour empêcher l’insertion de données actualisées, effectuez une redirection de page vers la même page ou une page différente après l’insertion de l’enregistrement. 

en-tête ('Emplacement:'. $ _ SERVER ['PHP_SELF']);

2
Thinesh

En gros, vous devez rediriger la page en dehors de cette page, mais cela peut toujours poser un problème lorsque votre internet est lent (En-tête de redirection à partir de serveride)

Exemple de scénario de base:

Cliquez deux fois sur le bouton d'envoi

Moyen de résoudre

  • Côté client

    • Désactiver le bouton d'envoi une fois que le client a cliqué dessus
    • Si vous utilisez Jquery: Jquery.one
    • Modèle PRG
  • Du côté serveur

    • Utilisation d’horodatage/horodatage de hachage différenciés lorsque la demande a été envoyée.
    • Jetons Userequest. Lorsque la machine principale se charge, attribuez un jeton de requête temporaire qui, s'il est répété, est ignoré.
2
ZenithS

Comment empêcher la resoumission de formulaire php sans redirection. Si vous utilisez $ _SESSION (après session_start) et un formulaire $ _POST, vous pouvez faire quelque chose comme ceci:

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

Dans votre formulaire html, mettez ceci:

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

Ainsi, chaque fois que le formulaire est soumis, un nouvel acte est généré, stocké en session et comparé au post-acte.

Ps: si vous utilisez un formulaire Get, vous pouvez facilement changer tout POST avec GET et cela fonctionne aussi.

1
Rômulo Z. C. Cunha

Utiliser la réponse Post/Redirect/Get de Keverw est une bonne idée. Cependant, vous ne pouvez pas rester sur votre page (et je pense que c’est ce que vous demandiez?). En outre, cela peut parfois échouer :

Si un utilisateur Web est actualisé avant la fin de la soumission initiale en raison d'un décalage du serveur, entraînant la duplication d'une demande HTTP POST dans certains agents utilisateurs.

Une autre option serait de stocker dans une session si le texte devait être écrit dans votre base de données SQL comme ceci:

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

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

Comme d'autres l'ont dit, il n'est pas possible de sortir de post/redirect/get. Mais en même temps, il est assez facile de faire ce que vous voulez faire côté serveur.

Dans votre page POST, vous ne faites que valider la saisie de l'utilisateur mais vous n'agissez pas sur celle-ci. Vous la copiez plutôt dans un tableau SESSION. Vous êtes ensuite redirigé vers la page de soumission principale. Votre page de soumission principale commence par vérifier si le tableau SESSION que vous utilisez existe et, le cas échéant, copiez-le dans un tableau local et supprimez-le. À partir de là, vous pouvez agir.

De cette façon, vous ne réaliserez qu'une seule fois tout votre travail principal, en réalisant ce que vous voulez faire.

0
Stu

J'ai cherché une solution pour empêcher la resoumission dans un projet énorme par la suite . Le code fonctionne fortement avec $ _GET et $ _POST et je ne peux pas changer le comportement des éléments de formulaire sans le risque de bugs imprévus. mon code:

<!-- 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"] );

Cela fonctionne seulement pour éviter la nouvelle soumission de $ _POST, pas de $ _GET. Mais c’est le comportement dont j’ai besoin… .. Le problème de resoumission ne fonctionne pas avec les téléchargements de fichiers!

0
Andreas Rex