Ngăn gửi lại biểu mẫu


107

Trang một chứa một biểu mẫu HTML. Trang hai - mã xử lý dữ liệu đã gửi.

Biểu mẫu ở trang một sẽ được gửi. Trình duyệt được chuyển hướng đến trang hai. Trang hai xử lý dữ liệu đã gửi.

Tại thời điểm này, nếu trang hai được làm mới, một cảnh báo "Xác nhận gửi lại biểu mẫu" sẽ bật lên.

Điều này có thể được ngăn chặn?


Sử dụng một hộp thoại xác nhận: stackoverflow.com/questions/6457750/form-confirm-before-submit/...

3
sử dụng chuyển hướng bài đăng nhận được như mô tả ở đây - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer

Bạn có thể ngăn chặn điều đó ngay cả khi không chuyển hướng. Nhìn đây
Eugen Konkov

Câu trả lời:


145

Có 2 cách tiếp cận mà mọi người thường áp dụng ở đây:

Phương pháp 1: Sử dụng AJAX + Chuyển hướng

Bằng cách này, bạn đăng biểu mẫu của mình ở chế độ nền bằng JQuery hoặc thứ gì đó tương tự như Page2, trong khi người dùng vẫn thấy trang1 được hiển thị. Sau khi đăng thành công, bạn chuyển hướng trình duyệt đến Trang2.

Phương pháp 2: Đăng + Chuyển hướng đến bản thân

Đây là một kỹ thuật phổ biến trên các diễn đàn. Biểu mẫu trên Trang1 đăng dữ liệu lên Trang2, Trang2 xử lý dữ liệu và thực hiện những gì cần phải làm, sau đó tự nó thực hiện chuyển hướng HTTP. Bằng cách này, "hành động" cuối cùng mà trình duyệt ghi nhớ là GET đơn giản trên trang2, vì vậy biểu mẫu sẽ không được gửi lại khi F5.


2
Một biến thể của phương pháp 2 là chuyển hướng đến một trang khác. Ví dụ: bạn ĐĂNG đến example.com/save.html và sau khi lưu xong, bạn chuyển hướng đến example.com/list.html .
Guillaume

1
Với Phương pháp 1, tôi sẽ sử dụng plugin jQuery có tên BlockUI để cho khách hàng biết điều gì đó đang xảy ra.
Shikiryu

Tôi đang sử dụng phương pháp 2 nhưng nó yêu cầu tôi gửi lại nếu tôi làm mới nó. Bất cứ ai gặp vấn đề tương tự với điều này?
quan tâm

Bạn có chắc chắn rằng chuyển hướng HTTP đang hoạt động bình thường không? Mở trình kiểm tra mạng / firebug / bất cứ thứ gì trình duyệt của bạn có để kiểm tra các yêu cầu HTTP gửi đi và kiểm tra các cuộc gọi HTTP. Bạn sẽ thấy điều đầu tiên (đăng dữ liệu biểu mẫu) xảy ra với phương thức POST, trả về mã HTTP 301 với tiêu đề Vị trí trỏ đến chính nó và ngay lập tức bạn sẽ thấy một truy vấn HTTP khác đến cùng một trang với phương thức GET.
CodeTwice

@CodeTwice: Trong trường hợp của tôi, trang1 đăng lên trang2, trang2 xử lý dữ liệu và hiển thị một trang (với các giá trị được chuyển đến mẫu) .. Việc chuyển hướng sẽ xảy ra ở đâu? .. Trang không có yêu cầu GET.
user1050619

24

Bạn cần sử dụng mẫu PRG - Post / Redirect / Get và bạn vừa triển khai P của PRG. Bạn cần phải Chuyển hướng . (Ngày nay bạn không cần chuyển hướng nữa. Hãy xem phần này )

PRG là một mẫu thiết kế phát triển web ngăn chặn một số lần gửi biểu mẫu trùng lặp, có nghĩa là, Gửi biểu mẫu (Yêu cầu đăng 1) -> Chuyển hướng -> Nhận (Yêu cầu 2)

Under the hood

Mã trạng thái chuyển hướng - HTTP 1.0 với HTTP 302 hoặc HTTP 1.1 với HTTP 303

Một phản hồi HTTP với mã trạng thái chuyển hướng sẽ cung cấp thêm một URL trong trường tiêu đề vị trí. Tác nhân người dùng (ví dụ: trình duyệt web) được mời bởi một phản hồi với mã này để thực hiện yêu cầu thứ hai, nếu không giống hệt, đến URL mới được chỉ định trong trường vị trí.

Mã trạng thái chuyển hướng là để đảm bảo rằng trong tình huống này, trình duyệt của người dùng web có thể làm mới phản hồi của máy chủ một cách an toàn mà không khiến yêu cầu HTTP POST ban đầu được gửi lại.

Double Submit Problem

Sự cố gửi hai lần

Post/Redirect/Get Solution

Đăng / Chuyển hướng / Nhận giải pháp

Nguồn


2
Lời giải thích hay. Tôi đang tự hỏi điều gì sẽ xảy ra nếu người dùng nhấp vào nút quay lại trên trình duyệt sau khi chuyển hướng được thực hiện. Liệu cửa sổ bật lên "xác nhận gửi lại biểu mẫu" có hiển thị lại không. Tôi đoán ngay cả khi cửa sổ bật lên xác nhận không hiển thị lại, một yêu cầu đăng trên trang 1 với cùng dữ liệu sẽ được thực hiện lại và một lần nữa người dùng sẽ được chuyển hướng đến trang 2. Tôi có một loạt biểu mẫu, form1, form2, form3. làm thế nào có thể quay lại từ dạng "n" thành dạng "n-1" một cách liền mạch. Câu hỏi ngẫu nhiên: Trong trường hợp bạn đã nghiên cứu các mẫu thiết kế PRG
Rpant

@Rpant trên chrome, tệp php gửi tiêu đề vị trí thậm chí không hiển thị trong lịch sử trình duyệt, vì vậy nút quay lại chỉ đơn giản là đưa bạn trở lại biểu mẫu.
FluorescentGreen

@Angelin Sau khi chuyển hướng get sẽ lấy dữ liệu như thế nào? Ví dụ: khi tôi đăng một số dữ liệu lên / some_url và chuyển hướng đến / some_url, điều này tạo ra yêu cầu GET. Làm cách nào để biết đâu là phản hồi vì / some_url là chung và không tạo ra một id như / some_url / <id>?
Amit Tripathi

@AmitTripathi bạn phải tự tạo. ví dụ: Yêu cầu POST có thể là chèn dữ liệu khách hàng trong khi GET sẽ hiển thị dữ liệu được chèn thành công. Bạn sẽ có thể hiển thị dữ liệu dưới dạng không có gì khác ngoài dữ liệu biểu mẫu hoặc sử dụng lại chế độ xem của trang khách hàng bằng cách tìm nạp từ cơ sở dữ liệu cho khách hàng đó theo id khách hàng mà bạn nhận được sau khi chèn thành công vào cơ sở dữ liệu.
Angelin Nadar,

Nhưng tôi đồng ý với giải pháp @Eugen Konkov
Angelin Nadar,

10

Trực tiếp, bạn không thể, và đó là một điều tốt. Cảnh báo của trình duyệt là có lý do. Chủ đề này sẽ trả lời câu hỏi của bạn:

Ngăn nút Quay lại hiển thị cảnh báo xác nhận ĐĂNG

Hai cách giải quyết chính được đề xuất là mô hình PRG và một bản đệ trình AJAX, sau đó là việc chuyển đổi tập lệnh.

Lưu ý rằng nếu phương thức của bạn cho phép GET chứ không phải phương thức gửi POST, thì điều đó sẽ giải quyết được vấn đề và phù hợp hơn với quy ước. Các giải pháp đó được cung cấp trên giả định bạn muốn / cần ĐĂNG dữ liệu.


8

Cách duy nhất để chắc chắn 100% rằng cùng một biểu mẫu không bao giờ được gửi hai lần là nhúng một số nhận dạng duy nhất vào mỗi biểu mẫu bạn phát hành và theo dõi những biểu mẫu nào đã được gửi tại máy chủ. Cạm bẫy ở đó là nếu người dùng sao lưu trang nơi có biểu mẫu và nhập dữ liệu mới, biểu mẫu tương tự sẽ không hoạt động.


6

Có hai phần cho câu trả lời:

  1. Đảm bảo các bài đăng trùng lặp không làm ảnh hưởng đến dữ liệu của bạn ở phía máy chủ. Để thực hiện việc này, hãy nhúng một số nhận dạng duy nhất vào bài đăng để bạn có thể từ chối các yêu cầu tiếp theo phía máy chủ. Mẫu này được gọi là Bộ thu lý tưởng trong thuật ngữ nhắn tin.

  2. Đảm bảo người dùng không bị làm phiền bởi khả năng gửi trùng lặp bởi cả hai

    • chuyển hướng đến GET sau POST (mẫu GET chuyển hướng POST)
    • vô hiệu hóa nút bằng javascript

Không có gì bạn làm dưới 2. sẽ hoàn toàn ngăn chặn việc gửi trùng lặp. Mọi người có thể nhấp rất nhanh và tin tặc có thể đăng bài. Bạn luôn cần 1. nếu bạn muốn chắc chắn rằng không có bản sao.


2

Nếu bạn làm mới một trang có dữ liệu ĐĂNG, trình duyệt sẽ xác nhận việc gửi lại của bạn. Nếu bạn sử dụng dữ liệu GET, thông báo sẽ không được hiển thị. Bạn cũng có thể có trang thứ hai, sau khi lưu bài nộp, chuyển hướng đến trang thứ ba không có dữ liệu.


2

Tôi thấy không ai đề cập đến thủ thuật này.

Nếu không chuyển hướng, bạn vẫn có thể ngăn xác nhận biểu mẫu khi làm mới.

Theo mặc định, mã biểu mẫu như sau:

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

bây giờ, thay đổi nó thành <form method="post" action="test.php?nonsense=1">

Bạn sẽ thấy điều kỳ diệu.

Tôi đoán là do trình duyệt sẽ không kích hoạt cửa sổ bật lên cảnh báo xác nhận nếu nó nhận được phương thức GET (chuỗi truy vấn) trong url.


2

Bạn có thể làm điều này bằng cách sử dụng jquery

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

Đây là cách tốt nhất để ngăn chặn dữ liệu trở lại sau khi gửi do đã đăng lại.

Hi vọng điêu nay co ich.


0

Mẫu PRG chỉ có thể ngăn việc gửi lại do làm mới trang. Đây không phải là biện pháp an toàn 100%.

Thông thường, tôi sẽ thực hiện các hành động bên dưới để ngăn việc gửi lại:

  1. Phía khách hàng - Sử dụng javascript để ngăn các nhấp chuột trùng lặp vào một nút sẽ kích hoạt gửi biểu mẫu. Bạn chỉ có thể vô hiệu hóa nút sau lần nhấp đầu tiên.

  2. Phía máy chủ - Tôi sẽ tính toán một hàm băm trên các tham số đã gửi và lưu hàm băm đó trong phiên hoặc cơ sở dữ liệu, vì vậy khi nhận được bản gửi trùng lặp, chúng tôi có thể phát hiện sự trùng lặp sau đó phản hồi thích hợp cho máy khách. Tuy nhiên, bạn có thể quản lý để tạo một hàm băm ở phía máy khách.

Trong hầu hết các trường hợp, các biện pháp này có thể giúp ngăn chặn việc gửi lại.


0

Tôi thực sự thích câu trả lời của @ Angelin. Nhưng nếu bạn đang xử lý một số mã cũ mà điều này không thực tế, thì kỹ thuật này có thể phù hợp với bạn.

Ở đầu tệp

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

Sau đó, ở cuối biểu mẫu, hãy ghi last_pos_subnhư sau:

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

0

Hãy thử 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;
    }
}

Cách sử dụng / ví dụ:

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

Phông chữ: https://www.tutdepot.com/prevent-multiple-form-submission/


0

sử dụng js để ngăn thêm dữ liệu:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.