Yêu cầu CORS POST hoạt động từ JavaScript thuần túy, nhưng tại sao lại không với jQuery?


87

Tôi đang cố gắng thực hiện một yêu cầu bài đăng Cross Origin và tôi đã làm cho nó hoạt động đơn giản JavaScriptnhư sau:

var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);

Nhưng tôi muốn sử dụng jQuery, nhưng tôi không thể làm cho nó hoạt động. Đây là những gì tôi đang cố gắng:

$.ajax(url, {
    type:"POST",
    dataType:"json",
    data:{action:"something"}, 
    success:function(data, textStatus, jqXHR) {alert("success");},
    error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Điều này dẫn đến Thất bại. Nếu ai biết tại sao jQuerykhông hoạt động, xin vui lòng cho chúng tôi biết. Cảm ơn.

(Tôi đang sử dụng jQuery1.5.1 và Firefox 4.0 và máy chủ của tôi đang phản hồi với Access-Control-Allow-Origintiêu đề phù hợp )


Đây là giải pháp cho tôi (sử dụng XMLHttpRequest của Javascript) trong khi đối mặt với các vấn đề CORS với Ionic framework 3.
jeudyx

Câu trả lời:


73

CẬP NHẬT: Như TimK đã chỉ ra, điều này không cần thiết với jquery 1.5.2 nữa. Nhưng nếu bạn muốn thêm tiêu đề tùy chỉnh hoặc cho phép sử dụng thông tin xác thực (tên người dùng, mật khẩu hoặc cookie, v.v.), hãy đọc tiếp.


Tôi nghĩ rằng tôi đã tìm thấy câu trả lời! (4 giờ và rất nhiều lời nguyền rủa sau đó)

//This does not work!!
Access-Control-Allow-Headers: *

Bạn cần chỉ định thủ công tất cả các tiêu đề mà bạn sẽ chấp nhận (ít nhất đó là trường hợp của tôi trong FF 4.0 & Chrome 10.0.648.204).

Phương thức $ .ajax của jQuery gửi tiêu đề "x-request-with" cho tất cả các yêu cầu tên miền chéo (tôi nghĩ tên miền chéo duy nhất của nó).

Vì vậy, tiêu đề bị thiếu cần thiết để phản hồi yêu cầu OPTIONS là:

//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with

Nếu bạn đang chuyển bất kỳ tiêu đề nào không "đơn giản", bạn sẽ cần đưa chúng vào danh sách của mình (tôi gửi thêm một tiêu đề nữa):

//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by

Vì vậy, để tổng hợp tất cả lại với nhau, đây là PHP của tôi:

// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Max-Age: 604800');
  //if you need special headers
  header('Access-Control-Allow-Headers: x-requested-with');
  exit(0);
}

5
Lưu ý rằng jQuery 1.5.2 đã thay đổi hành vi của nó. Nó không còn thêm tiêu đề "Đã yêu cầu X", vì vậy đây có thể không còn là vấn đề nữa. blog.jquery.com/2011/03/31/jquery-152-released (Bug 8423)
Magmatic

1
@TimK, bạn nói đúng! Tôi không nhận thấy họ phát hành 1.5.2. Điều này đang được nói, điều này cũng hoạt động nếu bạn cần nó được bay trước. Tôi đã cập nhật câu trả lời của mình.
Will Mason

Vì vậy, tôi bối rối. Cuối cùng bạn vẫn phải viết một tập lệnh PHP trung gian? Vì vậy, bạn không cần phải lo lắng về việc sử dụng Ajax sau đó, phải không? Hay tôi đang thiếu cái gì đó. Không có giải pháp duy nhất JavaScript?
Elisabeth

1
@Elisabeth Phương pháp này chỉ hoạt động nếu bạn kiểm soát đích được yêu cầu ... nó KHÔNG phải là một tập lệnh trung gian. Đây là phần trên cùng của PHP của chúng tôi về vị trí được yêu cầu. Điều đó có làm cho nó ý nghĩa hơn không?
Will Mason

2
Đúng! cảm ơn Will. Tôi nghĩ bạn có thể kiểm soát mọi thứ từ phía khách hàng, nhưng có vẻ như bạn cần kiểm soát cả hai đầu.
Elisabeth

18

Một khả năng khác là cài đặt dataType: jsonkhiến JQuery gửi Content-Type: application/jsontiêu đề. Đây được coi là tiêu đề không chuẩn của CORS và yêu cầu yêu cầu khởi động trước CORS. Vì vậy, một số điều cần thử:

1) Thử định cấu hình máy chủ của bạn để gửi các phản hồi thích hợp trước khi bay. Điều này sẽ ở dạng các tiêu đề bổ sung như Access-Control-Allow-MethodsAccess-Control-Allow-Headers.

2) Bỏ dataType: jsoncài đặt. JQuery nên yêu cầu Content-Type: application/x-www-form-urlencodedtheo mặc định, nhưng để chắc chắn, bạn có thể thay thế dataType: jsonbằngcontentType: 'application/x-www-form-urlencoded'


Cảm ơn vì những ý tưởng. Tôi đã cố gắng không đặt dataType và đặt nó thành application/x-www-form-urlencodedvà thậm chí text/plain. Và tôi đã thử thêm tiêu đề phản hồi là Access-Control-Allow-Methods "POST, GET, OPTIONS"Không có gì hoạt động.
Magmatic

Bạn có thể xem trong bảng điều khiển lỗi JavaScript (hoặc bảng điều khiển của Firebug) và xem liệu có bất kỳ lỗi nào trong quá trình yêu cầu không? Ngoài ra, nếu bạn biết cách sử dụng Wireshark, bạn có thể sử dụng nó để xem các yêu cầu HTTP thực tế đang truyền qua dây.
ông chủ

1
"Một khả năng khác là thiết lập dataType: json khiến JQuery gửi tiêu đề Content-Type: application / json" - Điều này không xảy ra. dataTypeảnh hưởng đến Accepttiêu đề yêu cầu nhưng không ảnh hưởng đến tiêu đề Content-Typeyêu cầu.
Quentin

9

Bạn đang gửi "params" bằng js: request.send(params);

nhưng "dữ liệu" trong jquery ". Dữ liệu có được định nghĩa không ?: data:data,

Ngoài ra, bạn gặp lỗi trong URL:

$.ajax( {url:url,
         type:"POST",
         dataType:"json",
         data:data, 
         success:function(data, textStatus, jqXHR) {alert("success");},
         error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Bạn đang trộn cú pháp với cú pháp cho $ .post


Cập nhật : Tôi đã tìm kiếm xung quanh dựa trên câu trả lời của ông và tôi thấy rằng bạn cần bổ sung Access-Control-Allow-Headers: Content-Type(bên dưới là đoạn văn đầy đủ)

http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

Cách hoạt động của CORS

CORS hoạt động rất giống với tệp crossdomain.xml của Flash. Về cơ bản, trình duyệt sẽ gửi một yêu cầu tên miền chéo đến một dịch vụ, đặt tiêu đề HTTP Origin cho máy chủ yêu cầu. Dịch vụ bao gồm một số tiêu đề như Access-Control-Allow-Origin để cho biết liệu một yêu cầu như vậy có được phép hay không.

Đối với trình quản lý kết nối BOSH, chỉ cần cho phép tất cả nguồn gốc là đủ, bằng cách đặt giá trị của Access-Control-Allow-Origin thành *. Tiêu đề Loại-Nội dung cũng phải được liệt kê trong danh sách trắng trong tiêu đề Access-Control-Allow-Headers.

Cuối cùng, đối với một số loại yêu cầu, bao gồm cả yêu cầu trình quản lý kết nối BOSH, việc kiểm tra quyền sẽ được thực hiện trước. Trình duyệt sẽ thực hiện yêu cầu TÙY CHỌN và mong đợi nhận lại một số tiêu đề HTTP cho biết nguồn gốc nào được phép, phương thức nào được phép và thời hạn ủy quyền này. Ví dụ: đây là những gì các bản vá Punjab và ejabberd tôi đã trả lại cho TÙY CHỌN:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400

1
Lấy làm tiếc. Đúng. var data = {action:"something"}
Magmatic

Bạn có thể so sánh cú pháp cho cả hai hàm tại đây: api.jquery.com/jQuery.post
Aleadam

Tôi vừa thử nó với url trong cài đặt, nhưng cùng một vấn đề. Hàm .ajax có thể thực hiện theo một trong hai cách.
Magmatic

Tôi đã có hai trong số những tiêu đề. Tôi đã thêm hai cái còn lại. Vẫn "thất bại" với jQuery. Javascript đơn giản vẫn hoạt động.
Magmatic

Điều cuối cùng tôi có thể nghĩ đến là sử dụng api.jquery.com/jQuery.ajaxSetup để thiết lập jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})và chơi với các tiêu đề khác nhau được gửi (ví dụ cho đường ray ở đây: railscasts.com/episodes/136-jquery )
Aleadam

1

Cors thay đổi phương thức yêu cầu trước khi hoàn tất, từ POST thành OPTIONS, do đó, dữ liệu bài đăng của bạn sẽ không được gửi. Cách hoạt động để xử lý vấn đề cors này là thực hiện yêu cầu với ajax, không hỗ trợ phương pháp OPTIONS. mã ví dụ:

        $.ajax({
            type: "POST",
            crossdomain: true,
            url: "http://localhost:1415/anything",
            dataType: "json",
            data: JSON.stringify({
                anydata1: "any1",
                anydata2: "any2",
            }),
            success: function (result) {
                console.log(result)
            },
            error: function (xhr, status, err) {
                console.error(xhr, status, err);
            }
        });

với tiêu đề này trên máy chủ c #:

                    if (request.HttpMethod == "OPTIONS")
                    {
                          response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                          response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                          response.AddHeader("Access-Control-Max-Age", "1728000");
                    }
                    response.AppendHeader("Access-Control-Allow-Origin", "*");

-2

Sửa đổi Jquery của bạn theo cách sau:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});

Tại sao tôi muốn làm cho các callas Ajax của mình đồng bộ !?
Radko Dinev

contentType: 'application/json', data: JSONObject,- Máy chủ không mong đợi JSON, vì vậy việc gửi JSON sẽ không có ý nghĩa. Ngoài ra, không có thứ gọi là Đối tượng JSON .
Quentin

1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Đừng bao giờ làm thế. Access-Control-Allow-Originlà một tiêu đề phản hồi , không phải là một tiêu đề yêu cầu. Tốt nhất điều này sẽ không làm gì cả. Tệ nhất là nó sẽ chuyển đổi yêu cầu từ một yêu cầu đơn giản thành một yêu cầu được đánh dấu trước, điều này khiến nó khó xử lý hơn trên máy chủ.
Quentin
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.