Proyek Django saya memiliki sejumlah tombol pada halaman web yang melakukan permintaan POST ke view.py utama yang pada gilirannya menangani tindakan dan mengembalikan respons 204 No content. Hasil tindakan muncul secara asinkron nanti di halaman web (pada saat respons dibuat, tidak ada yang baru untuk ditampilkan). Pada browser berbasis non-iOS, respons 204 berfungsi dengan baik dan halaman web tetap berada di browser, seperti yang diharapkan dari RFC 7231. Sayangnya semua browser berbasis iOS yang saya coba (Safari, Firefox, Chrome) menavigasi ke halaman kosong setelah POST, yang bukan itu yang saya inginkan (lihat pertanyaan ini). Rupanya ini adalah bug di WebKit yang sudah lama berdiri.

Apakah ada cara untuk mencapai hal yang sama di semua browser? yaitu. Klik tombol, POST, halaman web tetap apa adanya, perubahan muncul nanti. Saya telah melihat ini tetapi tidak yakin itu benar-benar apa yang saya kejar. Mengubah kode respons dimungkinkan tetapi saya tidak melihat alternatif yang layak yang tidak menavigasi keluar dari halaman saat ini. Perbaikan peretasan saya saat ini adalah memuat ulang seluruh halaman untuk perangkat iOS, tetapi ini memindahkan halaman jika pengguna telah menggulir ke bawah sebelumnya sehingga terlihat cukup tersendat.

0
Paul 29 Maret 2020, 07:49

1 menjawab

Jawaban Terbaik

Solusi untuk ini terdapat dalam pertanyaan ini dan jawabannya oleh Terry. Ditambah beberapa info lain tentang menangani perlindungan CSRF di Django.

Jawaban singkatnya adalah mengganti pengiriman formulir biasa dengan event.preventDefault() dalam fungsi onsubmit Anda sendiri untuk setiap formulir. Kemudian lakukan POST secara asinkron dengan JQuery. Ini menghindari server harus mengembalikan HttpResponse dengan status 204, atau apa pun, di luar JsonResponse kosong.

Ada beberapa formulir di halaman web saya jadi saya tidak memiliki satu fungsi pun yang ditetapkan untuk setiap tombol, seperti pada jawaban tertaut di atas, tetapi beberapa yang mengubah perilaku tombol individual. Juga untuk Django ada pertimbangan tambahan untuk memasukkan token CSRF dalam permintaan POST.

Jadi, misalnya, beberapa html dengan dua tombol;

<form id="form-a" onsubmit="submit_form_a(event)">{% csrf_token %}
    <input type="hidden" name="button-a" value="some_value_for_a">
    <button type="submit" class="w3-button">Button A</button>
</form>

<form id="form-z" onsubmit="submit_form_z(event)">{% csrf_token %}
    <input type="hidden" name="button-z" value="some_value_for_z">
    <button type="submit" class="w3-button">Button Z</button>
</form>

Kemudian Javascript, di mana formulir pertama mengirimkan panggilan balik, misalnya, menonaktifkan tombol;

function submit_form_a(e){
    e.preventDefault();
    var url = "/main/a"
    var formData = $(e.target).serialize();
    var btn = $(e.target).find("button");
    btn.prop("disabled", true);
    myPost(url, formData);
}

Dan panggilan balik pengiriman kedua yang melakukan beberapa animasi, menggunakan W3.CSS yang sangat baik;

function submit_form_z(e){
   e.preventDefault();
   var url = "/main/z"
   var formData = $(e.target).serialize();
   var btn = $(e.target).find("button");
   btn.toggleClass("w3-animate-fading").toggleClass("w3-text-red")
   myPost(url, formData);
}

Kedua skrip tersebut memanggil skrip umum mypost, yang menyiapkan header XMLHttpResponse dengan token CSRF, lalu POST;

// Make sure any AJAX POST requests are not going off-site. Prevents
// leaking the CSRF token.

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } 

function myPost(url, data) {
    var csrftoken = $("[name=csrfmiddlewaretoken]").val();
    // Set the header on the AJAX POST request with the CSRF token
    $.ajaxSetup({beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }});
    $.post(url, data);
}

Kembali ke sisi Django views.py, lihat data <input> dari formulir dan lakukan apa yang perlu Anda lakukan. Saya baru saja menemukan di iOS tombol itu sendiri tidak disertakan dalam data formulir POST, seperti yang terlihat pada platform lain, membuat elemen input teks tersembunyi diperlukan;

if request.is_ajax() and request.method == 'POST':
    if 'button-a' in request.POST:
        # do something for A
    elif 'button-z' in request.POST:
      # do something for Z
    return JsonResponse({})

Dan voila! Tombol dapat diklik, hal-hal latar belakang terjadi dan halaman tidak bergerak. Anda dapat mengembalikan sesuatu selain respons JSON kosong dan menanganinya kembali dalam bentuk panggilan balik, seperti mengaktifkan kembali tombol, tetapi saya tidak membutuhkannya. Lihat jawaban tertaut sebelumnya untuk detail di sana.

0
Paul 13 April 2020, 01:37