Uniemożliwienie ponownego przesłania formularza

107

Pierwsza strona zawiera formularz HTML. Strona druga - kod obsługujący przesłane dane.

Formularz na pierwszej stronie zostanie przesłany. Przeglądarka zostanie przekierowana na drugą stronę. Strona druga obsługuje przesłane dane.

W tym momencie, jeśli strona druga zostanie odświeżona, pojawi się komunikat „Potwierdź ponowne przesłanie formularza”.

Czy można temu zapobiec?

Emanuil Rusev
źródło
3
użyj przekierowania postu get zgodnie z opisem tutaj - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer
Możesz temu zapobiec nawet bez przekierowania. Spójrz tutaj
Eugen Konkov

Odpowiedzi:

145

Istnieją 2 podejścia, które ludzie stosowali tutaj:

Metoda 1: Użyj przekierowania AJAX +

W ten sposób wysyłasz swój formularz w tle za pomocą JQuery lub czegoś podobnego do Page2, podczas gdy użytkownik nadal widzi wyświetlaną stronę 1. Po pomyślnym wysłaniu przekierowujesz przeglądarkę do strony 2.

Metoda 2: Opublikuj + przekierowanie do siebie

Jest to powszechna technika na forach. Formularz na Stronie 1 wysyła dane do Strony 2, Strona 2 przetwarza dane i robi to, co trzeba, a następnie dokonuje przekierowania HTTP na siebie. W ten sposób ostatnią „czynnością”, jaką przeglądarka pamięta, jest proste GET na stronie 2, więc formularz nie jest ponownie przesyłany po naciśnięciu klawisza F5.

CodeTwice
źródło
2
Wariant metody 2 to przekierowanie na inną stronę. Na przykład, wysyłasz POST do example.com/save.html i po zakończeniu zapisywania przekierowujesz do example.com/list.html .
Guillaume,
1
W przypadku metody 1 użyłbym wtyczki jQuery o nazwie BlockUI, aby klient wiedział, że coś się dzieje.
Shikiryu
Używam metody 2, ale prosi mnie o ponowne przesłanie, jeśli ją odświeżę. Czy ktoś ma podobny problem?
chętny
Czy jesteś pewien, że przekierowanie HTTP działa poprawnie? Otwórz inspektora sieci / firebug / cokolwiek ma twoja przeglądarka do inspekcji wychodzących żądań HTTP i sprawdź wywołania HTTP. Powinieneś zobaczyć, że pierwsza (wysyłanie danych formularza) dzieje się z metodą POST, zwracając kod HTTP 301 z nagłówkiem Location wskazującym na siebie, a następnie od razu powinieneś zobaczyć kolejne zapytanie HTTP do tej samej strony z metodą GET.
CodeTwice,
@CodeTwice: W moim przypadku strona 1 wysyła posty na stronę 2, strona 2 przetwarza dane i wyświetla stronę (z wartościami przekazywanymi do szablonu) .. Gdzie powinno nastąpić przekierowanie? .. Strona nie ma żądań GET.
user1050619
24

Musisz użyć wzorca PRG - Post / Redirect / Get i właśnie zaimplementowałeś P z PRG. Musisz przekierować . (W dni, kiedy w ogóle nie potrzebujesz przekierowania. Zobacz to )

PRG to wzorzec projektowania stron internetowych, który zapobiega niektórym zduplikowanym przesyłaniu formularzy, co oznacza, Prześlij formularz (Post Request 1) -> Redirect -> Get (Request 2)

Under the hood

Kod statusu przekierowania - HTTP 1.0 z HTTP 302 lub HTTP 1.1 z HTTP 303

Odpowiedź HTTP z kodem statusu przekierowania będzie dodatkowo zawierać adres URL w polu nagłówka lokalizacji. Agent użytkownika (np. Przeglądarka internetowa) jest proszony przez odpowiedź z tym kodem, aby wykonać drugie, poza tym identyczne, żądanie do nowego adresu URL określonego w polu lokalizacji.

Kod stanu przekierowania ma zapewnić, że w tej sytuacji przeglądarka użytkownika sieci WWW może bezpiecznie odświeżyć odpowiedź serwera bez powodowania ponownego przesyłania początkowego żądania HTTP POST.

Double Submit Problem

Problem z podwójnym przesłaniem

Post/Redirect/Get Solution

Opublikuj / przekieruj / uzyskaj rozwiązanie

Źródło

Angelin Nadar
źródło
2
Dobre wytłumaczenie. Zastanawiam się, co się stanie, jeśli użytkownik kliknie przycisk Wstecz w przeglądarce po dokonaniu przekierowania. Czy wyskakujące okienko „Potwierdź ponowne przesłanie formularza” pojawi się ponownie. Wydaje mi się, że nawet jeśli wyskakujące okienko z potwierdzeniem nie pojawi się ponownie, żądanie wpisu na stronie 1 z tymi samymi danymi zostanie ponownie wysłane i ponownie użytkownik zostanie przekierowany na stronę 2. Mam szereg formularzy, form1, form2, form3. jak płynnie wrócić z formy „n” do postaci „n-1”. Losowe pytanie: Gdzie studiowałeś wzorzec projektowy PRG
Rpant
@Rpant na chrome plik php, który wysyła nagłówek lokalizacji, nie pojawia się nawet w historii przeglądarki, więc przycisk Wstecz po prostu przenosi cię z powrotem do formularza.
FluorescentGreen5
@Angelin Po przekierowaniu jak get dostanie dane? Na przykład, gdy wysyłam jakieś dane do / some_url i przekierowuję do / some_url, który wysyła żądanie GET. Skąd mam wiedzieć, jaka powinna być odpowiedź, ponieważ / some_url jest ogólna i nie zawiera identyfikatora takiego jak / some_url / <id>?
Amit Tripathi
@AmitTripathi musisz stworzyć siebie. np .: Żądanie POST może polegać na wprowadzeniu danych klienta, podczas gdy GET będzie wyświetlać dane wprowadzone pomyślnie. Będziesz mógł pokazać dane, ponieważ były to tylko dane formularza lub ponownie wykorzystać widok strony klienta, pobierając z bazy danych dla tego klienta według identyfikatora klienta, który otrzymałeś po pomyślnym wstawieniu do bazy danych.
Angelin Nadar
Ale zgadzam się z rozwiązaniem @Eugen Konkov
Angelin Nadar
10

Bezpośrednio nie możesz i to dobrze. Alert przeglądarki nie ma powodu. Ten wątek powinien odpowiedzieć na Twoje pytanie:

Zapobiegaj wyświetlaniu przez przycisk Wstecz alertu potwierdzającego POST

Dwa kluczowe sugerowane obejścia to wzorzec PRG i przesłanie AJAX, po którym nastąpiło przeniesienie skryptu.

Zwróć uwagę, że jeśli Twoja metoda zezwala na metodę przesyłania GET, a nie POST, to zarówno rozwiązałoby problem, jak i lepiej pasowałoby do konwencji. Te rozwiązania są dostarczane przy założeniu, że chcesz / potrzebujesz POST dane.

davin
źródło
8

Jedynym sposobem, aby mieć 100% pewności, że ten sam formularz nigdy nie zostanie przesłany dwukrotnie, jest osadzenie unikalnego identyfikatora w każdym wydanym formularzu i śledzenie, które z nich zostały przesłane na serwer. Pułapka polega na tym, że jeśli użytkownik utworzy kopię zapasową na stronie, na której znajdował się formularz, i wprowadzi nowe dane, ten sam formularz nie zadziała.

Blrfl
źródło
6

Odpowiedź składa się z dwóch części:

  1. Upewnij się, że zduplikowane posty nie psują danych po stronie serwera. Aby to zrobić, umieść w poście unikalny identyfikator, abyś mógł odrzucać kolejne żądania po stronie serwera. Ten wzorzec jest nazywany Idempotentnym Odbiornikiem w terminologii wiadomości.

  2. Upewnij się, że użytkownikowi nie przeszkadza możliwość podwójnego przesłania przez oba

    • przekierowanie do GET po POST (wzorzec przekierowania POST GET)
    • wyłączenie przycisku za pomocą javascript

Żadne czynności, które wykonujesz poniżej 2., nie zapobiegną zduplikowanym przesyłaniu. Ludzie mogą klikać bardzo szybko, a hakerzy i tak mogą publikować. Zawsze potrzebujesz 1., jeśli chcesz mieć absolutną pewność, że nie ma duplikatów.

iwein
źródło
2

Jeśli odświeżysz stronę z danymi POST, przeglądarka potwierdzi ponowne przesłanie. Jeśli korzystasz z danych GET, komunikat nie zostanie wyświetlony. Możesz również mieć drugą stronę, po zapisaniu zgłoszenia, przekierować na trzecią stronę bez danych.

Joel
źródło
2

Cóż, nikt nie wspomniał o tej sztuczce.

Bez przekierowania nadal możesz zapobiec potwierdzaniu formularza podczas odświeżania.

Domyślnie kod formularza wygląda tak:

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

teraz zmień to na <form method="post" action="test.php?nonsense=1">

Zobaczysz magię.

Wydaje mi się, że to dlatego, że przeglądarki nie wywołają wyskakującego okienka z alertem potwierdzenia, jeśli otrzyma metodę GET (ciąg zapytania) w adresie URL.

Atrakcja
źródło
2

Możesz to zrobić za pomocą jquery

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

Jest to najbardziej elegancki sposób zapobiegania ponownemu przesyłaniu danych po przesłaniu z powrotem.

Mam nadzieję że to pomoże.

noobprogrammer
źródło
0

Wzorzec PRG może jedynie zapobiec ponownemu przesłaniu spowodowanemu odświeżeniem strony. Nie jest to w 100% bezpieczny środek.

Zazwyczaj podejmę poniższe działania, aby zapobiec ponownemu przesłaniu:

  1. Strona klienta - użyj javascript, aby zapobiec zduplikowanym kliknięciom przycisku, który spowoduje przesłanie formularza. Możesz po prostu wyłączyć przycisk po pierwszym kliknięciu.

  2. Strona serwera - obliczę hash na przesłanych parametrach i zapiszę ten hash w sesji lub w bazie danych, aby po otrzymaniu zduplikowanego zgłoszenia można było wykryć duplikację, a następnie właściwą odpowiedź do klienta. Możesz jednak wygenerować skrót po stronie klienta.

W większości przypadków środki te mogą pomóc w zapobieganiu ponownemu złożeniu wniosku.

ehe888
źródło
0

Naprawdę podoba mi się odpowiedź @ Angelin. Ale jeśli masz do czynienia z jakimś starszym kodem, w którym nie jest to praktyczne, ta technika może Ci pomóc.

U góry pliku

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

Następnie na końcu formularza wkleić w last_pos_subnastępujący sposób:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Scott C Wilson
źródło
0

Wypróbuj 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;
    }
}

Jak używać / przykład:

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

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

Rômulo ZC Cunha
źródło
0

użyj js, aby zapobiec dodawaniu danych:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Tran Anh Hien
źródło