Mencegah pengiriman ulang formulir

107

Halaman pertama berisi formulir HTML. Halaman dua - kode yang menangani data yang dikirimkan.

Formulir di halaman pertama dikirimkan. Browser dialihkan ke halaman dua. Halaman dua menangani data yang dikirimkan.

Pada titik ini, jika halaman dua disegarkan, peringatan "Konfirmasi Pengiriman Ulang Formulir" akan muncul.

Bisakah ini dicegah?

Emanuil Rusev
sumber
3
gunakan pengalihan pos dapatkan seperti yang dijelaskan di sini - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer
Anda dapat mencegahnya bahkan tanpa pengalihan. Lihat di sini
Eugen Konkov

Jawaban:

145

Ada 2 pendekatan yang biasa dilakukan orang di sini:

Metode 1: Gunakan AJAX + Redirect

Dengan cara ini Anda memposting formulir Anda di latar belakang menggunakan JQuery atau sesuatu yang mirip dengan Page2, sementara pengguna masih melihat halaman1 ditampilkan. Setelah pengeposan berhasil, Anda mengarahkan browser ke Halaman2.

Metode 2: Poskan + Alihkan ke diri sendiri

Ini adalah teknik umum di forum. Formulir di Halaman1 memposting data ke Halaman2, Halaman2 memproses data dan melakukan apa yang perlu dilakukan, dan kemudian melakukan pengalihan HTTP pada dirinya sendiri. Dengan cara ini, "tindakan" terakhir yang diingat browser adalah GET sederhana di halaman2, jadi formulir tidak dikirim kembali ke F5.

CodeTwice
sumber
2
Variasi dari metode 2 adalah pengalihan ke halaman lain. Misalnya, Anda POST ke example.com/save.html dan setelah penyimpanan selesai, Anda dialihkan ke example.com/list.html .
Guillaume
1
Dengan Metode 1, saya akan menggunakan plugin jQuery bernama BlockUI untuk memberi tahu klien bahwa sesuatu sedang terjadi.
Shikiryu
Saya menggunakan metode 2 tetapi meminta saya untuk mengirim ulang lagi jika saya menyegarkannya. Adakah yang mengalami masalah serupa dengan ini?
tertarik
Apakah Anda yakin bahwa pengalihan HTTP berfungsi dengan baik? Buka pemeriksa jaringan / firebug / apa pun yang dimiliki browser Anda untuk memeriksa permintaan HTTP keluar dan periksa panggilan HTTP. Anda akan melihat yang pertama (memposting data formulir) terjadi dengan metode POST, mengembalikan kode HTTP 301 dengan header Lokasi mengarah ke dirinya sendiri, dan Anda akan segera melihat kueri HTTP lain ke halaman yang sama dengan metode GET.
CodeTwice
@ CodeTwice: Dalam kasus saya, halaman1 memposting ke halaman2, halaman2 memproses data dan menampilkan halaman (dengan nilai yang diteruskan ke template) .. Di mana pengalihan harus dilakukan? .. Halaman tersebut tidak memiliki permintaan GET.
user1050619
24

Anda perlu menggunakan PRG - Post / Redirect / Get pola dan Anda baru saja mengimplementasikan P dari PRG. Anda perlu Redirect . (Sekarang hari Anda tidak perlu pengalihan sama sekali. Lihat ini )

PRG adalah pola desain pengembangan web yang mencegah beberapa pengiriman formulir duplikat yang artinya, Kirim formulir (Kirim Permintaan 1) -> Redirect -> Dapatkan (Permintaan 2)

Under the hood

Kode status pengalihan - HTTP 1.0 dengan HTTP 302 atau HTTP 1.1 dengan HTTP 303

Respons HTTP dengan kode status pengalihan juga akan memberikan URL di bidang tajuk lokasi. Agen pengguna (misalnya browser web) diundang oleh respons dengan kode ini untuk membuat permintaan kedua, jika tidak identik, ke URL baru yang ditentukan di bidang lokasi.

Kode status pengalihan adalah untuk memastikan bahwa dalam situasi ini, browser pengguna web dapat menyegarkan respons server dengan aman tanpa menyebabkan permintaan HTTP POST awal dikirim ulang.

Double Submit Problem

Masalah Pengiriman Ganda

Post/Redirect/Get Solution

Pasang / Alihkan / Dapatkan Solusi

Sumber

Angelin Nadar
sumber
2
Penjelasan yang bagus. Saya bertanya-tanya apa yang akan terjadi jika pengguna mengklik tombol kembali di browser setelah pengalihan dibuat. Akankah popup "konfirmasi pengiriman ulang formulir" muncul lagi. Saya rasa bahkan jika popup konfirmasi tidak muncul lagi, permintaan posting di halaman 1 dengan data yang sama dibuat lagi dan sekali lagi pengguna akan diarahkan ke halaman 2. Saya memiliki serangkaian formulir, formulir1, formulir2, formulir3. bagaimana bisa kembali dari bentuk "n" ke bentuk "n-1" dengan mulus. Pertanyaan acak: Di mana Anda mempelajari pola desain PRG
Rpant
@Rpant di chrome file php yang mengirimkan header lokasi bahkan tidak muncul di riwayat browser, jadi tombol kembali hanya membawa Anda kembali ke formulir.
FluorescentGreen5
@Angelin Setelah redirect bagaimana mendapatkan akan mendapatkan data? Misalnya ketika saya memposting beberapa data ke / some_url dan mengarahkan ke / some_url yang membuat permintaan GET. Bagaimana saya tahu apa yang seharusnya menjadi respons karena / some_url adalah generik dan tidak menghubungkan id seperti / some_url / <id>?
Amit Tripathi
@AmitTripathi Anda harus membuat sendiri. Misal: Permintaan POST bisa berupa memasukkan data pelanggan sedangkan GET akan menampilkan data yang berhasil dimasukkan. Anda akan dapat menampilkan data karena tidak lain adalah data formulir atau menggunakan kembali tampilan halaman pelanggan dengan mengambil dari database untuk pelanggan tersebut dengan id pelanggan yang Anda terima setelah berhasil memasukkan ke dalam database.
Angelin Nadar
Tapi saya setuju dengan solusi @Eugen Konkov
Angelin Nadar
10

Secara langsung, Anda tidak bisa, dan itu hal yang baik. Peringatan browser ada karena suatu alasan. Utas ini harus menjawab pertanyaan Anda:

Mencegah tombol Kembali menampilkan peringatan konfirmasi POST

Dua solusi utama yang disarankan adalah pola PRG, dan pengiriman AJAX diikuti dengan relokasi skrip.

Perhatikan bahwa jika metode Anda memungkinkan metode pengiriman GET dan bukan metode pengiriman POST, itu akan menyelesaikan masalah dan lebih sesuai dengan konvensi. Solusi tersebut disediakan dengan asumsi Anda ingin / perlu data POST.

davin
sumber
8

Satu-satunya cara untuk 100% yakin bahwa formulir yang sama tidak pernah dikirimkan dua kali adalah dengan menyematkan pengenal unik di setiap yang Anda terbitkan dan melacak mana yang telah dikirimkan di server. Kesulitannya adalah jika pengguna kembali ke halaman tempat formulir itu dan memasukkan data baru, formulir yang sama tidak akan berfungsi.

Blrfl
sumber
6

Ada dua bagian untuk menjawabnya:

  1. Pastikan postingan duplikat tidak mengacaukan data Anda di sisi server. Untuk melakukan ini, sematkan pengenal unik di posting sehingga Anda dapat menolak sisi server permintaan berikutnya. Pola ini disebut Penerima Idempoten dalam istilah perpesanan.

  2. Pastikan pengguna tidak terganggu oleh kemungkinan pengiriman duplikat oleh keduanya

    • mengarahkan ke GET setelah POST (pola GET pengalihan POST)
    • menonaktifkan tombol menggunakan javascript

Tidak ada yang Anda lakukan di bawah 2. benar-benar akan mencegah pengiriman duplikat. Orang dapat mengklik dengan sangat cepat dan peretas tetap dapat memposting. Anda selalu membutuhkan 1. jika Anda ingin benar-benar yakin tidak ada duplikat.

iwein
sumber
2

Jika Anda menyegarkan halaman dengan data POST, browser akan mengkonfirmasi pengiriman ulang Anda. Jika Anda menggunakan GET data, pesan tersebut tidak akan ditampilkan. Anda juga dapat memiliki halaman kedua, setelah menyimpan kiriman, mengarahkan ke halaman ketiga tanpa data.

Joel
sumber
2

Yah saya tidak menemukan ada yang menyebutkan trik ini.

Tanpa pengalihan, Anda masih dapat mencegah konfirmasi formulir saat menyegarkan.

Secara default, kode formulir seperti ini:

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

sekarang, ubah menjadi <form method="post" action="test.php?nonsense=1">

Anda akan melihat keajaiban.

Saya kira itu karena browser tidak akan memicu popup peringatan konfirmasi jika mendapat metode GET (string kueri) di url.

Menyoroti
sumber
2

Anda dapat melakukannya dengan menggunakan jquery

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

Ini adalah cara paling elegan untuk mencegah data kembali setelah dikirimkan karena posting kembali.

Semoga ini membantu.

noobprogrammer
sumber
0

Pola PRG hanya dapat mencegah pengiriman ulang yang disebabkan oleh penyegaran halaman. Ini bukan tindakan yang 100% aman.

Biasanya, saya akan mengambil tindakan di bawah untuk mencegah pengiriman ulang:

  1. Sisi Klien - Gunakan javascript untuk mencegah klik ganda pada tombol yang akan memicu pengiriman formulir. Anda bisa menonaktifkan tombol setelah klik pertama.

  2. Sisi Server - Saya akan menghitung hash pada parameter yang dikirimkan dan menyimpan hash itu dalam sesi atau database, sehingga ketika pengiriman duplikat diterima, kami dapat mendeteksi duplikasi kemudian memberikan respons yang tepat kepada klien. Namun, Anda dapat membuat hash di sisi klien.

Di sebagian besar kesempatan, tindakan ini dapat membantu mencegah pengiriman ulang.

ehe888
sumber
0

Saya sangat suka jawaban @ Angelin. Tetapi jika Anda berurusan dengan beberapa kode lama di mana ini tidak praktis, teknik ini mungkin berhasil untuk Anda.

Di bagian atas 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'];
     }
}

Kemudian di akhir formulir, tempelkan last_pos_subsebagai berikut:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Scott C Wilson
sumber
0

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

Cara menggunakan / contoh:

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

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

Rômulo ZC Cunha
sumber
0

gunakan js untuk mencegah penambahan data:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Tran Anh Hien
sumber