Impedire il reinvio del modulo


107

La prima pagina contiene un modulo HTML. Pagina due: il codice che gestisce i dati inviati.

Il modulo nella prima pagina viene inviato. Il browser viene reindirizzato alla pagina due. La seconda pagina gestisce i dati inviati.

A questo punto, se la pagina due viene aggiornata, viene visualizzato un avviso "Conferma reinvio modulo".

Questo può essere prevenuto?



3
usa post redirect get come descritto qui - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer

Puoi impedirlo anche senza reindirizzamento. Guarda qui
Eugen Konkov

Risposte:


145

Ci sono 2 approcci che le persone usavano qui:

Metodo 1: usa AJAX + Redirect

In questo modo pubblichi il tuo modulo in background usando JQuery o qualcosa di simile a Page2, mentre l'utente continua a vedere la pagina1 visualizzata. Dopo aver pubblicato con successo, reindirizzi il browser a Pagina2.

Metodo 2: Post + Reindirizza a se stesso

Questa è una tecnica comune nei forum. Il modulo su Pagina1 invia i dati a Pagina2, Pagina2 elabora i dati e fa ciò che deve essere fatto, quindi esegue un reindirizzamento HTTP su se stesso. In questo modo l'ultima "azione" che il browser ricorda è un semplice GET a pagina2, quindi il modulo non viene ripresentato su F5.


2
Una variante del metodo 2 è un reindirizzamento a un'altra pagina. Ad esempio, POST su example.com/save.html e una volta terminato il salvataggio, reindirizzi a example.com/list.html .
Guillaume

1
Con il metodo 1, userei il plugin jQuery chiamato BlockUI per far sapere al client che sta succedendo qualcosa.
Shikiryu

Sto usando il metodo 2 ma mi chiede di inviarlo nuovamente se lo aggiorno. Qualcuno ha problemi simili a questo?
appassionato

Sei sicuro che il reindirizzamento HTTP funzioni correttamente? Apri l'ispettore di rete / firebug / qualunque cosa abbia il tuo browser per ispezionare le richieste HTTP in uscita e controlla le chiamate HTTP. Dovresti vedere il primo (pubblicare i dati del modulo) accadere con il metodo POST, restituire il codice HTTP 301 con l'intestazione Location che punta a se stesso, e quindi immediatamente dovresti vedere un'altra query HTTP sulla stessa pagina con il metodo GET.
CodeTwice

@CodeTwice: Nel mio caso, page1 invia a page2, page2 elabora i dati e mostra una pagina (con valori passati al modello) .. Dove dovrebbe avvenire il reindirizzamento? .. La pagina non ha richieste GET.
user1050619

24

Devi usare PRG - Post / Redirect / Get pattern e hai appena implementato il P di PRG. Devi reindirizzare . (Oggigiorno non è più necessario il reindirizzamento. Vedi questo )

PRG è un modello di progettazione di sviluppo web che impedisce l'invio di moduli duplicati, il che significa Invia modulo (richiesta post 1) -> Reindirizzamento -> Ottieni (richiesta 2)

Under the hood

Codice di stato di reindirizzamento : HTTP 1.0 con HTTP 302 o HTTP 1.1 con HTTP 303

Una risposta HTTP con codice di stato di reindirizzamento fornirà inoltre un URL nel campo dell'intestazione della posizione. Il programma utente (ad esempio un browser web) è invitato da una risposta con questo codice a fare una seconda richiesta, altrimenti identica, al nuovo URL specificato nel campo della posizione.

Il codice di stato di reindirizzamento serve a garantire che in questa situazione, il browser dell'utente Web possa aggiornare in sicurezza la risposta del server senza causare il reinvio della richiesta HTTP POST iniziale.

Double Submit Problem

Doppio problema di invio

Post/Redirect/Get Solution

Pubblica / Reindirizza / Ottieni soluzione

fonte


2
Buona spiegazione. Mi chiedo cosa succederà se l'utente fa clic sul pulsante Indietro del browser dopo aver effettuato il reindirizzamento. Il popup "Conferma reinvio modulo" verrà visualizzato di nuovo. Immagino che anche se il popup di conferma non viene più visualizzato, una richiesta di post a pagina 1 con gli stessi dati verrà eseguita di nuovo e ancora una volta l'utente verrà reindirizzato alla pagina 2. Ho una serie di moduli, form1, form2, form3. come si può tornare dalla forma "n" alla forma "n-1" senza soluzione di continuità. Domanda casuale: dove hai studiato il modello di progettazione PRG
Rpant

@Rpant su chrome il file php che invia l'intestazione della posizione non viene nemmeno visualizzato nella cronologia del browser, quindi il pulsante indietro ti riporta semplicemente al modulo.
FluorescentGreen5

@Angelin Dopo il reindirizzamento come get otterrà i dati? Ad esempio, quando invio alcuni dati a / some_url e reindirizzo a / some_url che fa una richiesta GET. Come faccio a sapere quale dovrebbe essere la risposta poiché / some_url è generico e non contiene un ID come / some_url / <id>?
Amit Tripathi

@AmitTripathi devi creare te stesso. es: la richiesta POST potrebbe essere di inserimento dei dati del cliente mentre GET sarebbe la visualizzazione dei dati inseriti con successo. Sarai in grado di mostrare i dati come se fossero nient'altro che i dati del modulo o riutilizzare la visualizzazione della pagina del cliente recuperando dal database per quel cliente dall'ID cliente che hai ricevuto dopo l'inserimento riuscito nel database.
Angelin Nadar il

Ma sono d'accordo con la soluzione di @Eugen Konkov
Angelin Nadar il

10

Direttamente, non puoi, e questa è una buona cosa. L'avviso del browser è presente per un motivo. Questo thread dovrebbe rispondere alla tua domanda:

Impedisci al pulsante Indietro di mostrare l'avviso di conferma POST

Due soluzioni chiave suggerite sono state il modello PRG e un invio AJAX seguito da un trasferimento dello script.

Nota che se il tuo metodo consente un metodo di invio GET e non POST, questo risolverebbe il problema e si adatterebbe meglio alla convenzione. Tali soluzioni vengono fornite supponendo che si desideri / necessiti di POST dei dati.


8

L'unico modo per essere sicuri al 100% che lo stesso modulo non venga mai inviato due volte è incorporare un identificatore univoco in ciascuno di essi e tenere traccia di quelli inviati al server. L'inconveniente è che se l'utente esegue il backup nella pagina in cui si trovava il modulo e inserisce nuovi dati, lo stesso modulo non funzionerà.


6

Ci sono due parti per la risposta:

  1. Assicurati che i post duplicati non interferiscano con i tuoi dati sul lato server. Per fare ciò, incorpora un identificatore univoco nel post in modo da poter rifiutare le richieste successive lato server. Questo modello è chiamato Idempotent Receiver in termini di messaggistica.

  2. Assicurati che l'utente non sia infastidito dalla possibilità di invii duplicati da parte di entrambi

    • reindirizzamento a un GET dopo il POST (schema GET di reindirizzamento POST)
    • disabilitare il pulsante utilizzando javascript

Niente di ciò che fai sotto 2. impedirà totalmente l'invio duplicato. Le persone possono fare clic molto velocemente e gli hacker possono comunque pubblicare. Hai sempre bisogno di 1. se vuoi essere assolutamente sicuro che non ci siano duplicati.


2

Se aggiorni una pagina con i dati POST, il browser confermerà il tuo reinvio. Se si utilizzano dati GET, il messaggio non verrà visualizzato. Potresti anche fare in modo che la seconda pagina, dopo aver salvato l'invio, reindirizzi a una terza pagina senza dati.


2

Beh, non ho trovato nessuno che abbia menzionato questo trucco.

Senza il reindirizzamento, puoi comunque impedire la conferma del modulo durante l'aggiornamento.

Per impostazione predefinita, il codice del modulo è così:

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

ora, cambialo in <form method="post" action="test.php?nonsense=1">

Vedrai la magia.

Immagino sia perché i browser non attiveranno il popup di avviso di conferma se ottiene un metodo GET (stringa di query) nell'URL.


2

Puoi farlo usando jquery

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

Questo è il modo più elegante per impedire che i dati vengano nuovamente inviati dopo l'invio a causa del postback.

Spero che questo ti aiuti.


0

Il pattern PRG può solo impedire il reinvio causato dall'aggiornamento della pagina. Questa non è una misura sicura al 100%.

Di solito, adotterò le azioni seguenti per impedire il reinvio:

  1. Lato client: utilizza javascript per evitare clic duplicati su un pulsante che attiveranno l'invio del modulo. Puoi semplicemente disabilitare il pulsante dopo il primo clic.

  2. Lato server: calcolerò un hash sui parametri inviati e salverò quell'hash nella sessione o nel database, in modo che quando l'invio duplicato è stato ricevuto possiamo rilevare la duplicazione e quindi la risposta corretta al client. Tuttavia, puoi riuscire a generare un hash sul lato client.

Nella maggior parte delle occasioni, queste misure possono aiutare a prevenire la ripresentazione.


0

Mi piace molto la risposta di @ Angelin. Ma se hai a che fare con codice legacy in cui questo non è pratico, questa tecnica potrebbe funzionare per te.

Nella parte superiore del file

// 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'];
     }
}

Quindi alla fine del modulo, attaccalo last_pos_subcome segue:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>

0

Prova tris:

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

Come usare / esempio:

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

Carattere: https://www.tutdepot.com/prevent-multiple-form-submission/


0

usa js per impedire l'aggiunta di dati:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.