Câu trả lời:
Không thể thực hiện việc không đồng bộ POST
vớ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.
POST
cá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 POST
và JSONP
không hợp nhau.
<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.
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:
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)
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ủ.
XMLHttpRequest
khô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.
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;
}
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.
CORS
thì đ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 JSON
hơn là JSONP
.
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ủ.
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?
<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).
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.")";