Posting data ke JsonP

102

Apakah mungkin untuk mengirim data ke JsonP? Atau apakah semua data harus diteruskan di querystring sebagai permintaan GET?

Saya memiliki banyak data yang harus saya kirim ke layanan, lintas domain, dan terlalu besar untuk dikirim melalui querystring

Apa sajakah pilihan untuk menyiasati ini?

ChrisCa
sumber

Jawaban:

83

Tidak mungkin melakukan asinkron POSTke layanan di domain lain, karena batasan (cukup masuk akal) dari kebijakan asal yang sama . JSON-P hanya berfungsi karena Anda diizinkan untuk memasukkan <script>tag ke DOM, dan mereka bisa menunjuk ke mana saja.

Anda dapat, tentu saja, membuat halaman di domain lain sebagai tindakan dari formulir biasa POST.

Sunting : Ada beberapa peretasan menarik di luar sana jika Anda bersedia melakukan banyak upaya untuk memasukkan <iframe>dan menyia-nyiakan properti mereka.

friedo
sumber
Anda menyebutkan bahwa "POST asinkron" tidak dimungkinkan .... lalu dapatkah saya melakukan POST sinkron?
Tandai
4
@mark "synchronous POST" berarti mengirimkan formulir yang menggunakan <form method = "post" action = "http: // ... / ...">
Steven Kryskalla
8
Ini tidak sepenuhnya benar. Anda pasti bisa melakukan POSTrequest ke domain lain selama domain tersebut dan browser Anda mendukung CORS. Tapi itu sepenuhnya benar POSTdan JSONPtidak kompatibel.
hippietrail
2
JSONP diimplementasikan dengan memasukkan <script>tag yang mengarah ke domain lain. Satu-satunya cara untuk menjalankan permintaan POST di browser adalah melalui formulir HTML atau XMLHttpRequest.
friedo
1
(secara umum -) Adalah mungkin (!) untuk melakukan POST asinkron ke layanan di domain lain. Batasannya ada pada respon. Batasannya juga ada pada permintaan JSONP.
Royi Namir
20

Jika Anda perlu mengirim banyak data lintas domain. Saya biasanya membuat layanan yang dapat Anda panggil dalam dua langkah:

  1. Pertama klien melakukan pengiriman FORM (posting diperbolehkan lintas domain). Layanan menyimpan input dalam sesi di server (menggunakan GUID sebagai kunci). (klien membuat GUID dan mengirimkannya sebagai bagian dari input)

  2. Kemudian klien melakukan suntikan-skrip normal (JSONP) sebagai parameter Anda menggunakan GUID yang sama seperti yang Anda gunakan dalam posting FORMULIR. Layanan memproses masukan dari sesi dan mengembalikan data dalam mode JSONP normal. Setelah sesi ini dihancurkan.

Ini tentu saja bergantung pada apa yang Anda tulis di server-backend.

Per
sumber
1
Mencoba pendekatan Anda. Bekerja untuk FF14 dan Chrome20. Opera11 dan IE9 tidak hanya mentransfer pos. (Memeriksa dengan alat debug mereka dan mendengarkan di server di ujung lain) Mungkin terkait dengan ketidakmampuan IE adalah pertanyaan ini: stackoverflow.com/questions/10395803/… keluhan Chrome di konsol, tetapi masih melakukan POST: XMLHttpRequest tidak bisa memuat localhost: 8080 / xxx Asal null tidak diperbolehkan oleh Access-Control-Allow-Origin.
OneWorld
@OneWorld - Anda tidak melakukan apa yang dikatakan jawabannya. XMLHttpRequestseharusnya tidak terlibat sama sekali. Jawaban Per menggunakan pengiriman formulir biasa untuk membuat permintaan POST, lalu injeksi elemen skrip untuk membuat permintaan GET.
Quentin
7

Saya tahu ini adalah necromancy yang serius, tetapi saya pikir saya akan memposting implementasi JSONP POST saya menggunakan jQuery, yang berhasil saya gunakan untuk widget JS saya (ini digunakan untuk pendaftaran dan login pelanggan):

Pada dasarnya, saya menggunakan pendekatan IFrame, seperti yang disarankan dalam jawaban yang diterima. Yang saya lakukan berbeda adalah setelah mengirim permintaan, saya perhatikan, jika formulir bisa dihubungi di iframe, menggunakan timer. Ketika formulir tidak dapat dihubungi, itu berarti permintaan telah dikembalikan. Kemudian, saya menggunakan permintaan JSONP normal untuk menanyakan status operasi.

Saya berharap seseorang merasakan manfaatnya. Diuji di> = IE8, Chrome, FireFox, dan Safari.

function JSONPPostForm(form, postUrl, queryStatusUrl, queryStatusSuccessFunc, queryStatusData)
{
    var tmpDiv = $('<div style="display: none;"></div>');
    form.parent().append(tmpDiv);
    var clonedForm = cloneForm(form);
    var iframe = createIFrameWithContent(tmpDiv, clonedForm);

    if (postUrl)
        clonedForm.attr('action', postUrl);

    var postToken = 'JSONPPOST_' + (new Date).getTime();
    clonedForm.attr('id', postToken);
    clonedForm.append('<input name="JSONPPOSTToken" value="'+postToken+'">');
    clonedForm.attr('id', postToken );
    clonedForm.submit();

    var timerId;
    var watchIFrameRedirectHelper = function()
    {
        if (watchIFrameRedirect(iframe, postToken ))
        {
            clearInterval(timerId);
            tmpDiv.remove();
            $.ajax({
                url:  queryStatusUrl,
                data: queryStatusData,
                dataType: "jsonp",
                type: "GET",
                success: queryStatusSuccessFunc
            });
        }
    }

    if (queryStatusUrl && queryStatusSuccessFunc)
        timerId = setInterval(watchIFrameRedirectHelper, 200);
}

function createIFrameWithContent(parent, content)
{
    var iframe = $('<iframe></iframe>');
    parent.append(iframe);

    if (!iframe.contents().find('body').length)
    {
        //For certain IE versions that do not create document content...
        var doc = iframe.contents().get()[0];
        doc.open();
        doc.close();
    }

    iframe.contents().find('body').append(content);
    return iframe;
}

function watchIFrameRedirect(iframe, formId)
{
    try
    {
        if (iframe.contents().find('form[id="' + formId + '"]').length)
            return false;
        else
            return true;
    }
    catch (err)
    {
        return true;
    }
    return false;
}

//This one clones only form, without other HTML markup
function cloneForm(form)
{
    var clonedForm = $('<form></form>');
    //Copy form attributes
    $.each(form.get()[0].attributes, function(i, attr)
    {
        clonedForm.attr(attr.name, attr.value);
    });
    form.find('input, select, textarea').each(function()
    {
        clonedForm.append($(this).clone());
    });

    return clonedForm;
}
WB
sumber
4

Umumnya JSONP diimplementasikan dengan menambahkan <script>tag ke dokumen pemanggil, sehingga URL layanan JSONP adalah "src". Browser mengambil sumber skrip dengan transaksi HTTP GET.

Sekarang, jika layanan JSONP Anda berada di domain yang sama dengan halaman panggilan Anda, maka Anda mungkin dapat menggabungkan sesuatu dengan $.ajax()panggilan sederhana . Jika tidak dalam domain yang sama, maka saya tidak yakin bagaimana mungkin.

Runcing
sumber
Ini tidak dalam domain yang sama dalam kasus ini. Dan saya berasumsi bahwa hanya GET yang mungkin, tetapi saya ingin memeriksanya karena saya baru mulai membaca tentang JsonP hari ini dan perlu membuat beberapa keputusan tentang apakah itu cocok untuk apa yang saya butuhkan
ChrisCa
2
Jika tidak dalam domain yang sama tetapi mendukung CORSmaka akan dimungkinkan selama browser juga mendukungnya. Dalam kasus ini Anda akan menggunakan polos JSONdaripada JSONP.
hippietrail
Ya, @hippietrail 2 tahun membuat perbedaan besar :-) CORS benar-benar memungkinkan, tetapi tentu saja hal itu membutuhkan sumber data yang disiapkan dengan tepat.
Pointy
0

Anda dapat menggunakan Proxy CORS menggunakan proyek ini . Ini akan mengarahkan semua lalu lintas ke titik akhir di domain Anda dan menyampaikan informasi tersebut ke domain eksternal. Karena browser mendaftarkan semua permintaan untuk berada di domain yang sama, kami dapat memposting JSON. CATATAN: Ini juga berfungsi dengan sertifikat SSL yang disimpan di server.

Eugene Scray
sumber
-1

Ada solusi (peretasan) yang saya lakukan berkali-kali, Anda akan dapat memposting dengan JsonP. (Anda akan dapat Mengirim Formulir, lebih besar dari 2000 karakter daripada yang dapat Anda gunakan oleh GET)

Javascript aplikasi klien

$.ajax({
  type: "POST", // you request will be a post request
  data: postData, // javascript object with all my params
  url: COMAPIURL, // my backoffice comunication api url
  dataType: "jsonp", // datatype can be json or jsonp
  success: function(result){
    console.dir(result);
  }
});

JAWA:

response.addHeader( "Access-Control-Allow-Origin", "*" ); // open your api to any client 
response.addHeader( "Access-Control-Allow-Methods", "POST" ); // a allow post
response.addHeader( "Access-Control-Max-Age", "1000" ); // time from request to response before timeout

PHP:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: 1000');

Melakukan seperti ini, Anda membuka server Anda untuk permintaan kiriman apa pun, Anda harus mengamankan kembali ini dengan memberikan ident atau sesuatu yang lain.

Dengan metode ini, Anda juga dapat mengubah jenis permintaan dari jsonp ke json, keduanya berfungsi, cukup setel jenis konten respons yang tepat

jsonp

response.setContentType( "text/javascript; charset=utf-8" );

json

response.setContentType( "application/json; charset=utf-8" );

Mohon tidak bahwa server Anda tidak akan lagi menghormati SOP (kebijakan asal yang sama), tapi siapa yang peduli?

Dimitri Kopriwa
sumber
Ini bukan AJAX dengan CORS. AJAX menyiratkan bahwa Anda menggunakan XML. Ini JSON [P] dengan CORS. JSONP adalah "JSON" dengan "Padding". Jika mengirim data JSON, dibungkus dengan panggilan fungsi untuk padding, maka itu adalah JSONP dengan CORS. Anda dapat menggunakan notasi data JSON dan JSONP di luar hanya memasukkan <script>tag ke DOM HTML Anda (bahkan Anda dapat menggunakannya di aplikasi desktop, katakanlah Anda ingin membuat beberapa permintaan JSON ke server yang sama, dan ingin menggunakan nama fungsi sebagai ID pelacakan permintaan misalnya).
BrainSlugs83
-6

Mungkin saja, inilah solusi saya:

Di javascript Anda:

jQuery.post("url.php",data).complete(function(data) {
    eval(data.responseText.trim()); 
});
function handleRequest(data){
    ....
}

Di url.php Anda:

echo "handleRequest(".$responseData.")";
nosemaj.dll
sumber
11
Dalam hal ini jQuery kemungkinan besar mengubah permintaan Anda menjadi Get sesuai dengan dokumentasinya: Catatan: Ini akan mengubah POST menjadi GET untuk permintaan domain jarak jauh. api.jquery.com/jQuery.ajax
OneWorld