Đăng dữ liệu lên JsonP


102

Có thể đăng dữ liệu lên JsonP không? Hay tất cả dữ liệu phải được chuyển vào chuỗi truy vấn dưới dạng yêu cầu GET?

Tôi có rất nhiều dữ liệu cần gửi tới dịch vụ, nhiều miền và nó quá lớn để gửi qua chuỗi truy vấn

Các tùy chọn để giải quyết vấn đề này là gì?

Câu trả lời:


83

Không thể thực hiện việc không đồng bộ POSTvới một dịch vụ trên một miền khác do giới hạn (khá hợp lý) của chính sách gốc . JSON-P chỉ hoạt động vì bạn được phép chèn<script> thẻ vào DOM và chúng có thể trỏ đến bất cứ đâu.

Tất nhiên, bạn có thể biến một trang trên tên miền khác thành hành động của một POST biểu mẫu thông thường.

Chỉnh sửa : Có một số thủ thuật thú vị trên mạng nếu bạn sẵn sàng bỏ ra nhiều công sức để chèn các ẩn <iframe>và dán các thuộc tính của chúng.


Bạn đã đề cập rằng "BÀI ĐĂNG không đồng bộ" là không thể .... sau đó tôi có thể thực hiện ĐĂNG đồng bộ không?
Đánh dấu

4
@mark "ĐỒNG BỘ ĐĂNG" có nghĩa là gửi biểu mẫu sử dụng <form method = "post" action = "http: // ... / ...">
Steven Kryskalla

8
Điều này hoàn toàn không đúng. Bạn chắc chắn có thể thực hiện POSTcác yêu cầu đối với các miền khác miễn là cả miền đó và trình duyệt của bạn hỗ trợ CORS. Nhưng điều đó hoàn toàn đúng POSTJSONPkhông hợp nhau.
hippietrail

2
JSONP được triển khai bằng cách chèn <script>các thẻ trỏ đến một miền khác. Cách duy nhất để thực thi yêu cầu POST trong trình duyệt là thông qua biểu mẫu HTML hoặc XMLHttpRequest.
Friedo

1
(nói chung -) Có thể (!) thực hiện POST không đồng bộ tới một dịch vụ trên miền khác. giới hạn là về phản hồi. giới hạn cũng là đối với yêu cầu JSONP.
Royi Namir

20

Nếu bạn cần gửi nhiều dữ liệu giữa nhiều miền. Tôi thường tạo một dịch vụ mà bạn có thể gọi trong hai bước:

  1. Trước tiên, khách hàng thực hiện gửi MẪU (được phép đăng trên nhiều miền). Dịch vụ lưu trữ đầu vào trong phiên trên máy chủ (sử dụng GUID làm khóa). (khách hàng tạo GUID và gửi nó như một phần của đầu vào)

  2. Sau đó, ứng dụng khách thực hiện chèn tập lệnh bình thường (JSONP) như một tham số mà bạn sử dụng cùng một GUID như bạn đã sử dụng trong bài đăng FORM. Dịch vụ xử lý đầu vào từ phiên và trả về dữ liệu theo kiểu JSONP bình thường. Sau đó, phiên này bị phá hủy.

Tất nhiên, điều này dựa vào việc bạn viết phần phụ trợ máy chủ.


1
Đã thử cách tiếp cận của bạn. Hoạt động cho FF14 và Chrome20. Opera11 và IE9 đã không chuyển bài đăng. (Đã kiểm tra nó bằng các công cụ gỡ lỗi của họ và lắng nghe trên máy chủ ở đầu bên kia) Có thể liên quan đến tình trạng khuyết tật của IE là câu hỏi này: stackoverflow.com/questions/10395803/… Khiếu nại về Chrome trong bảng điều khiển, nhưng vẫn không POST: XMLHttpRequest tải localhost: 8080 / xxx Origin null không được Access-Control-Allow-Origin cho phép.
OneWorld,

@OneWorld - Bạn đã không làm như câu trả lời. XMLHttpRequestkhông nên dính líu gì cả. Câu trả lời của Per sử dụng gửi biểu mẫu thông thường để thực hiện yêu cầu ĐĂNG, sau đó chèn phần tử tập lệnh để thực hiện yêu cầu GET.
Quentin

7

Tôi biết điều này là sai lầm nghiêm trọng, nhưng tôi nghĩ rằng tôi sẽ đăng việc triển khai JSONP POST bằng jQuery, mà tôi đang sử dụng thành công cho tiện ích JS của mình (cái này được sử dụng để đăng ký và đăng nhập của khách hàng):

Về cơ bản, tôi đang sử dụng phương pháp IFrame, như được đề xuất trong câu trả lời được chấp nhận. Điều tôi đang làm khác là sau khi gửi yêu cầu, tôi đang xem, nếu có thể truy cập biểu mẫu trong iframe, sử dụng bộ hẹn giờ. Khi không thể truy cập được biểu mẫu, có nghĩa là yêu cầu đã được trả lại. Sau đó, tôi đang sử dụng một yêu cầu JSONP bình thường để truy vấn trạng thái của hoạt động.

Tôi hy vọng rằng ai đó thấy nó hữu ích. Đã thử nghiệm trong> = IE8, Chrome, FireFox và 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

Nói chung JSONP được triển khai bằng cách thêm <script>thẻ vào tài liệu gọi, sao cho URL của dịch vụ JSONP là "src". Trình duyệt tìm nạp nguồn tập lệnh bằng giao dịch HTTP GET.

Bây giờ, nếu dịch vụ JSONP của bạn nằm trong cùng một miền với trang gọi của bạn, thì bạn có thể kết hợp điều gì đó với nhau bằng một $.ajax()cuộc gọi đơn giản . Nếu nó không thuộc cùng một miền, thì tôi không chắc làm thế nào nó có thể được.


Nó không nằm trong cùng một miền trong trường hợp này. Và tôi giả định rằng chỉ có thể GET, nhưng tôi muốn kiểm tra vì hôm nay tôi mới bắt đầu đọc về JsonP và cần đưa ra một số quyết định về việc liệu nó có phù hợp với những gì tôi cần hay không
ChrisCa

2
Nếu nó không thuộc cùng một miền nhưng nó hỗ trợ CORSthì điều đó có thể xảy ra miễn là trình duyệt cũng hỗ trợ nó. Trong những trường hợp này, bạn sẽ sử dụng đơn giản JSONhơn là JSONP.
hippietrail

Đúng, @hippietrail 2 năm tạo ra sự khác biệt lớn :-) CORS chắc chắn làm được điều đó, nhưng tất nhiên nó yêu cầu nguồn dữ liệu phải được thiết lập một cách thích hợp.
Pointy

0

Bạn có thể sử dụng Proxy CORS bằng cách sử dụng dự án này . Nó sẽ hướng tất cả lưu lượng truy cập đến một điểm cuối trên miền của bạn và chuyển tiếp thông tin đó đến một miền bên ngoài. Vì trình duyệt đang đăng ký tất cả các yêu cầu trên cùng một miền nên chúng tôi có thể đăng JSON. LƯU Ý: Điều này cũng hoạt động với chứng chỉ SSL được giữ trên máy chủ.


-1

Có một giải pháp (hack) mà tôi đã làm nhiều lần, bạn sẽ có thể Đăng bằng JsonP. (Bạn sẽ có thể Đăng Biểu mẫu, lớn hơn 2000 ký tự mà bạn có thể sử dụng bằng GET)

Ứng dụng khách 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');

Làm như vậy, bạn đang mở máy chủ của mình cho bất kỳ yêu cầu đăng bài nào, bạn nên bảo mật lại điều này bằng cách cung cấp danh tính hoặc thứ gì khác.

Với phương pháp này, bạn cũng có thể thay đổi loại yêu cầu từ jsonp thành json, cả hai đều hoạt động, chỉ cần đặt loại nội dung phản hồi phù hợp

jsonp

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

json

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

Xin lưu ý rằng máy chủ của bạn sẽ không tôn trọng SOP (chính sách nguồn gốc tương tự), nhưng ai quan tâm?


Đây không phải là AJAX với CORS. AJAX ngụ ý rằng bạn đang sử dụng XML. Đây là JSON [P] với CORS. JSONP là "JSON" với "Padding". Nếu nó đang gửi dữ liệu JSON, được bao bọc bằng một lệnh gọi hàm cho padding, thì đó là JSONP với CORS. Bạn có thể sử dụng cả ký hiệu dữ liệu JSON và JSONP ngoài việc chỉ chèn <script>thẻ vào HTML DOM của mình (thật tuyệt, bạn thậm chí có thể sử dụng chúng trong các ứng dụng dành cho máy tính để bàn, giả sử bạn muốn thực hiện nhiều yêu cầu JSON đến cùng một máy chủ và muốn sử dụng tên hàm làm ID theo dõi yêu cầu chẳng hạn).
BrainSlugs83

-6

Có thể, đây là giải pháp của tôi:

Trong javascript của bạn:

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

Trong url.php của bạn:

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

11
Trong trường hợp này, jQuery rất có thể đã chuyển yêu cầu của bạn thành Nhận theo tài liệu của họ: Lưu ý: Điều này sẽ biến POST thành GET cho các yêu cầu miền từ xa. api.jquery.com/jQuery.ajax
OneWorld
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.