โพสต์ข้อมูลไปยัง JsonP


102

สามารถโพสต์ข้อมูลไปยัง JsonP ได้หรือไม่? หรือข้อมูลทั้งหมดจะต้องถูกส่งผ่านในสตริงการสืบค้นเป็นคำขอ GET?

ฉันมีข้อมูลจำนวนมากที่ต้องส่งไปยังบริการข้ามโดเมนและมีขนาดใหญ่เกินไปที่จะส่งผ่านสตริงการสืบค้น

อะไรคือตัวเลือกสำหรับการหลีกเลี่ยงสิ่งนี้?

คำตอบ:


83

มันเป็นไปไม่ได้ที่จะทำไม่ตรงกันPOSTที่จะให้บริการในโดเมนอื่นเนื่องจากการ (ที่เหมาะสมมาก) ข้อ จำกัด ของนโยบายกำเนิดเดียวกัน JSON-P ใช้งานได้ก็ต่อเมื่อคุณได้รับอนุญาตให้แทรก<script>แท็กลงใน DOM และสามารถชี้ไปที่ใดก็ได้

แน่นอนคุณสามารถสร้างเพจบนโดเมนอื่นโดยใช้รูปแบบปกติ POST

แก้ไข : มีแฮ็กที่น่าสนใจบางอย่างอยู่ที่นั่นหากคุณเต็มใจที่จะใช้ความพยายามอย่างมากในการแทรก<iframe>s ที่ซ่อนอยู่และทำให้ยุ่งเกี่ยวกับคุณสมบัติของพวกเขา


คุณบอกว่า "โพสต์แบบอะซิงโครนัส" เป็นไปไม่ได้ .... แล้วฉันจะทำโพสต์แบบซิงโครนัสได้หรือไม่?
ทำเครื่องหมาย

4
@mark "synchronous POST" หมายถึงการส่งแบบฟอร์มที่ใช้ <form method = "post" action = "http: // ... / ... ">
Steven Kryskalla

8
นี่ไม่เป็นความจริงเสียทีเดียว แน่นอนคุณสามารถทำPOSTคำขอไปยังโดเมนอื่น ๆ CORSตราบใดที่ทั้งสองว่าโดเมนและการสนับสนุนเบราว์เซอร์ของคุณ แต่มันเป็นความจริงทั้งหมดว่าPOSTและJSONPจะไม่เข้ากัน
hippietrail

2
ใช้ JSONP โดยการใส่<script>แท็กที่ชี้ไปยังโดเมนอื่น วิธีเดียวในการดำเนินการคำขอ POST ในเบราว์เซอร์คือผ่านรูปแบบ HTML หรือ XMLHttpRequest
ทอด

1
(โดยทั่วไป -) เป็นไปได้ที่จะทำ POST แบบอะซิงโครนัสกับบริการบนโดเมนอื่น ข้อ จำกัด อยู่ที่การตอบสนอง ข้อ จำกัด ยังอยู่ในคำขอ JSONP
Royi Namir

20

หากคุณต้องการส่งข้อมูลจำนวนมากข้ามโดเมน โดยปกติฉันจะสร้างบริการที่คุณสามารถโทรได้ในสองขั้นตอน:

  1. ขั้นแรกให้ลูกค้าทำการส่งแบบฟอร์ม (อนุญาตให้โพสต์ข้ามโดเมน) บริการจัดเก็บอินพุตในเซสชันบนเซิร์ฟเวอร์ (โดยใช้ GUID เป็นคีย์) (ไคลเอนต์สร้าง GUID และส่งเป็นส่วนหนึ่งของอินพุต)

  2. จากนั้นไคลเอ็นต์จะทำการฉีดสคริปต์ (JSONP) ตามปกติเป็นพารามิเตอร์ที่คุณใช้ GUID เดียวกับที่คุณใช้ในโพสต์แบบฟอร์ม บริการประมวลผลอินพุตจากเซสชันและส่งคืนข้อมูลในรูปแบบ JSONP ปกติ หลังจากนี้เซสชันจะถูกทำลาย

แน่นอนว่านี่ขึ้นอยู่กับว่าคุณเขียนเซิร์ฟเวอร์แบ็กเอนด์


1
ลองใช้คำพูดของคุณ ทำงานสำหรับ FF14 และ Chrome20 Opera11 และ IE9 ไม่ได้ถ่ายโอนโพสต์ (ตรวจสอบด้วยเครื่องมือดีบักและฟังบนเซิร์ฟเวอร์ในตอนท้าย) คำถามนี้อาจเกี่ยวข้องกับความพิการของ IE: stackoverflow.com/questions/10395803/… Chrome ร้องเรียนในคอนโซล แต่ยังทำ POST: XMLHttpRequest ไม่ได้ โหลดlocalhost: 8080 / xxx Origin null ไม่ได้รับอนุญาตโดย Access-Control-Allow-Origin
OneWorld

@OneWorld - คุณไม่ได้ทำตามที่ตอบ XMLHttpRequestไม่ควรมีส่วนร่วมเลย คำตอบของ Per ใช้การส่งแบบฟอร์มปกติเพื่อสร้างคำขอ POST จากนั้นจึงฉีดองค์ประกอบสคริปต์เพื่อสร้างคำขอ GET
Quentin

7

ฉันรู้ว่านี่เป็นความผิดปกติที่ร้ายแรง แต่ฉันคิดว่าฉันจะโพสต์การใช้งาน JSONP POST โดยใช้ jQuery ซึ่งฉันใช้กับวิดเจ็ต JS ของฉันได้สำเร็จ (ใช้สำหรับการลงทะเบียนลูกค้าและเข้าสู่ระบบ):

โดยทั่วไปฉันใช้วิธี IFrame ตามที่แนะนำในคำตอบที่ยอมรับ สิ่งที่ฉันทำแตกต่างออกไปคือหลังจากส่งคำขอแล้วฉันกำลังเฝ้าดูว่าแบบฟอร์มสามารถเข้าถึงได้ใน iframe โดยใช้ตัวจับเวลา เมื่อไม่สามารถเข้าถึงแบบฟอร์มได้แสดงว่าคำขอกลับมาแล้ว จากนั้นฉันใช้คำขอ JSONP ปกติเพื่อสอบถามสถานะของการดำเนินการ

ฉันหวังว่าจะมีคนพบว่ามีประโยชน์ ผ่านการทดสอบใน> = IE8, Chrome, FireFox และ 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;
}

4

โดยทั่วไป JSONP จะถูกนำไปใช้โดยการเพิ่ม<script>แท็กลงในเอกสารการโทรดังนั้น URL ของบริการ JSONP คือ "src" เบราว์เซอร์ดึงซอร์สของสคริปต์ด้วยธุรกรรม HTTP GET

ตอนนี้หากบริการ JSONP ของคุณอยู่ในโดเมนเดียวกับหน้าการโทรของคุณคุณอาจจะสามารถใช้งานร่วมกันได้ด้วยการ$.ajax()โทรธรรมดา หากไม่ได้อยู่ในโดเมนเดียวกันฉันก็ไม่แน่ใจว่าจะเป็นไปได้อย่างไร


ในกรณีนี้ไม่ได้อยู่ในโดเมนเดียวกัน และฉันสมมติว่ามีเพียง GET เท่านั้นที่เป็นไปได้ แต่ต้องการตรวจสอบเนื่องจากฉันเพิ่งเริ่มอ่านเกี่ยวกับ JsonP ในวันนี้และต้องตัดสินใจบางอย่างว่าเหมาะสมกับสิ่งที่ฉันต้องการหรือไม่
ChrisCa

2
หากไม่ได้อยู่ในโดเมนเดียวกัน แต่รองรับCORSก็จะเป็นไปได้ตราบเท่าที่เบราว์เซอร์ยังรองรับ ในกรณีนี้คุณจะใช้ธรรมดามากกว่าJSON JSONP
hippietrail

ใช่ @hippietrail 2 ปีสร้างความแตกต่างอย่างมาก :-) CORS ทำให้เป็นไปได้อย่างแน่นอน แต่แน่นอนว่าต้องมีการตั้งค่าแหล่งข้อมูลอย่างเหมาะสม
Pointy

0

คุณสามารถใช้CORS Proxyโดยใช้โปรเจ็กต์นี้ ซึ่งจะนำการรับส่งข้อมูลทั้งหมดไปยังปลายทางบนโดเมนของคุณและส่งต่อข้อมูลนั้นไปยังโดเมนภายนอก เนื่องจากเบราว์เซอร์กำลังลงทะเบียนคำขอทั้งหมดให้อยู่ในโดเมนเดียวกันเราจึงสามารถโพสต์ JSON ได้ หมายเหตุ: ใช้งานได้กับใบรับรอง SSL ที่อยู่บนเซิร์ฟเวอร์


-1

มีวิธี (แฮ็ก) ที่ฉันเคยทำหลายครั้งคุณจะสามารถโพสต์ด้วย JsonP (คุณจะสามารถโพสต์แบบฟอร์มที่มีขนาดใหญ่กว่า 2,000 ตัวอักษรกว่าที่ GET ใช้ได้)

Javascript แอปพลิเคชันไคลเอนต์

$.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);
  }
});

Java:

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');

เมื่อทำเช่นนี้คุณกำลังเปิดเซิร์ฟเวอร์ของคุณเพื่อขอโพสต์ใด ๆ คุณควรรักษาความปลอดภัยอีกครั้งโดยการระบุรหัสหรืออย่างอื่น

ด้วยวิธีนี้คุณยังสามารถเปลี่ยนประเภทคำขอจาก jsonp เป็น json ได้ทั้งสองอย่างเพียงตั้งค่าประเภทเนื้อหาตอบกลับที่ถูกต้อง

jsonp

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

json

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

โปรดอย่าว่าเซิร์ฟเวอร์ของคุณจะไม่เคารพ SOP อีกต่อไป (นโยบายต้นทางเดียวกัน) แต่ใครจะสนล่ะ?


นี่ไม่ใช่ AJAX กับ CORS AJAX หมายความว่าคุณกำลังใช้ XML นี่คือ JSON [P] กับ CORS JSONP คือ "JSON" พร้อมด้วย "Padding" หากเป็นการส่งข้อมูล JSON โดยมีการเรียกใช้ฟังก์ชันสำหรับ padding แสดงว่าเป็น JSONP กับ CORS คุณสามารถใช้ทั้ง JSON และ JSONP ข้อมูลสัญกรณ์นอกเหนือจากการฉีด<script>แท็กลงใน HTML DOM ของคุณ (ห่าคุณยังสามารถใช้ในแอปเดสก์ท็อปได้ด้วยกล่าวว่าคุณต้องการส่งคำขอ JSON หลายรายการไปยังเซิร์ฟเวอร์เดียวกันและต้องการใช้ชื่อฟังก์ชัน เป็นรหัสติดตามคำขอเป็นต้น)
BrainSlugs83

-6

เป็นไปได้นี่คือทางออกของฉัน:

ในจาวาสคริปต์ของคุณ:

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

ใน url.php ของคุณ:

echo "handleRequest(".$responseData.")";

11
ในกรณีนี้ jQuery มักจะเปลี่ยนคำขอของคุณเป็น Get ตามเอกสารของพวกเขา: หมายเหตุ: สิ่งนี้จะเปลี่ยน POST ให้เป็น GET สำหรับคำขอโดเมนระยะไกล api.jquery.com/jQuery.ajax
OneWorld
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.