AJAX trong Chrome gửi TÙY CHỌN thay vì GET / POST / PUT / DELETE?


107

Tôi đang làm việc trên một ứng dụng web nội bộ tại nơi làm việc. Trong IE10, các yêu cầu hoạt động tốt, nhưng trong Chrome, tất cả các yêu cầu AJAX (có rất nhiều) được gửi bằng OPTIONS thay vì bất kỳ phương thức xác định nào mà tôi đưa ra. Về mặt kỹ thuật, yêu cầu của tôi là "tên miền chéo". Trang web được phục vụ trên localhost: 6120 và dịch vụ mà tôi đang thực hiện yêu cầu AJAX là trên 57124. Lỗi jquery đã đóng này xác định sự cố, nhưng không phải là bản sửa lỗi thực sự.

Tôi có thể làm gì để sử dụng phương thức http thích hợp trong các yêu cầu ajax?

Biên tập:

Đây là trong phần tải tài liệu của mọi trang:

jQuery.support.cors = true;

Và mọi AJAX đều được xây dựng giống nhau:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});

2
Nhận xét cuối cùng trong báo cáo lỗi đó giải thích nó khá tốt ...
Kevin B

1
Nó khiến tôi bối rối bởi vì mọi thứ tôi đang làm đều rất thô sơ (và mã của tôi tương tự như trong lỗi jquery). Điều đó sang một bên, không có lý do gì để không bao gồm nó. BRB, lấy một số mã mẫu.
Corey Ogburn

3
Lưu ý rằng IE không xem xét số cổng khi xác định xem một yêu cầu có phải là nguồn gốc chéo hay không.
Ray Nicholus

@KevinB: Dịch vụ REST của chúng tôi tận dụng các yêu cầu khác nhau để làm những việc khác nhau dựa trên phương thức http. Chuyển mọi thứ sang GET không phải là một bản sửa lỗi hợp lệ. Ngoài ra, theo câu trả lời của Dark Falcon, nó sẽ không giúp ích gì bởi vì tôi có X-UserName và các tiêu đề tùy chỉnh khác trong các yêu cầu.
Corey Ogburn

điều đó không thay đổi thực tế là nếu bạn muốn đưa ra một yêu cầu có nguồn gốc chéo, bạn phải tuân theo tất cả các quy tắc áp dụng cho các yêu cầu có nguồn gốc chéo để yêu cầu đó hoạt động bình thường. yêu cầu nguồn gốc chéo thường liên quan đến yêu cầu OPTIONS. Xử lý nó đúng cách và vấn đề sẽ biến mất. Cách khác duy nhất để giải quyết vấn đề này (mà không cần thay đổi api) là có một tập lệnh trên cùng một máy chủ như trang chính tương tác với api bằng cách sử dụng mã phía máy chủ.
Kevin B

Câu trả lời:


136

Chrome đang đánh dấu trước yêu cầu tìm kiếm các tiêu đề CORS . Nếu yêu cầu được chấp nhận, nó sẽ gửi yêu cầu thực sự. Nếu bạn đang thực hiện điều này giữa nhiều miền, bạn sẽ phải đối phó với nó hoặc tìm cách khác để thực hiện yêu cầu không liên miền. Đây là lý do tại sao lỗi jQuery bị đóng mà không thể sửa được. Đây là do thiết kế.

Không giống như các yêu cầu đơn giản (đã thảo luận ở trên), các yêu cầu "preflighted" trước tiên gửi một yêu cầu HTTP theo phương thức OPTIONS tới tài nguyên trên miền khác, để xác định xem yêu cầu thực sự có an toàn để gửi hay không. Yêu cầu trên nhiều trang web được đánh dấu trước như thế này vì chúng có thể có liên quan đến dữ liệu người dùng. Đặc biệt, một yêu cầu được đánh dấu trước nếu:

  • Nó sử dụng các phương thức khác ngoài GET, HEAD hoặc POST. Ngoài ra, nếu POST được sử dụng để gửi dữ liệu yêu cầu với Loại-Nội dung khác với application / x-www-form-urlencoded, multiart / form-data, hoặc văn bản / thuần túy, ví dụ: nếu yêu cầu POST gửi một trọng tải XML đến máy chủ sử dụng ứng dụng / xml hoặc văn bản / xml, sau đó yêu cầu được đánh dấu trước.
  • Nó đặt tiêu đề tùy chỉnh trong yêu cầu (ví dụ: yêu cầu sử dụng tiêu đề như X-PINGOTHER)

20
Tiêu đề tùy chỉnh. Đó có thể là những gì đang thiết lập các cuộc gọi OPTIONS trước khi khởi hành.
Corey Ogburn

18

Dựa trên thực tế là yêu cầu không được gửi trên cổng mặc định 80/443, cuộc gọi Ajax này tự động được coi là yêu cầu tài nguyên có nguồn gốc chéo (CORS) , nói cách khác là yêu cầu tự động đưa ra một yêu cầu OPTIONS để kiểm tra Tiêu đề CORS ở phía máy chủ / servlet.

Điều này xảy ra ngay cả khi bạn đặt

crossOrigin: false;

hoặc ngay cả khi bạn bỏ qua nó.

Lý do chỉ đơn giản là vậy localhost != localhost:57124. Hãy thử chỉ gửi nó đến localhostmà không có cổng - nó sẽ không thành công, vì mục tiêu được yêu cầu sẽ không thể truy cập được, tuy nhiên hãy lưu ý rằng nếu các tên miền bằng nhau , yêu cầu sẽ được gửi mà không có yêu cầu OPTIONS trước khi ĐĂNG.


3

Tôi đồng ý với Kevin B, báo cáo lỗi nói lên tất cả. Có vẻ như bạn đang cố thực hiện cuộc gọi ajax giữa nhiều miền. Nếu bạn không quen với chính sách nguồn gốc tương tự, bạn có thể bắt đầu tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Nếu đây không phải là lệnh gọi ajax giữa nhiều miền, hãy thử tạo url mục tiêu của bạn có liên quan và xem sự cố có biến mất không. Nếu bạn thực sự tuyệt vọng, hãy xem JSONP, nhưng hãy cẩn thận, tình trạng lộn xộn đang rình rập. Chúng tôi thực sự không thể làm gì nhiều hơn để giúp bạn.


1
Cấu trúc hệ thống của chúng tôi là thứ mà tôi không thể thay đổi. Sử dụng một cổng khác là một yêu cầu trong kiến ​​trúc của chúng tôi. Tôi nhận được chính sách xuất xứ tương tự nhưng nghĩ rằng CORS chúng tôi thực hiện là đủ. Rõ ràng là không.
Corey Ogburn

2
Nếu máy chủ của bạn trả về phản hồi JSON, bạn có thể xem xét phương thức JSONP, chỉ cần sử dụng nó một cách có trách nhiệm.
jgitter

1
Tôi không thực sự quan tâm đến việc tranh luận với bạn, nhưng JSONP sử dụng các thẻ script để lấy dữ liệu từ một miền khác và sau đó gửi kết quả đến một hàm gọi lại. Sẽ khó hơn rất nhiều nếu kết quả không phải là json.
jgitter

1
Không, nó không khó hơn nhiều. Trên thực tế, phản hồi không phải là JSON hợp lệ trong mọi trường hợp. Thay vào đó, máy chủ sẽ trả về một cái gì đó như thế này: callbackfunc(somedata). Như bạn có thể thấy, đây không phải là JSON hợp lệ. Và, somedatacó thể là một chuỗi, hoặc một số, hoặc bất cứ thứ gì bạn muốn.
Ray Nicholus

1
Tôi đang sử dụng Postman và ở đó các phương thức yêu cầu được gửi chính xác (ví dụ: 'PUT', 'DELETE', v.v.). Nhưng khi tôi cố gắng làm điều đó từ mã của mình, nó luôn gửi chúng với phương thức yêu cầu TÙY CHỌN. Tôi không biết làm thế nào mà Postman có thể làm được điều đó.
ErwinGO

1

Nếu có thể, hãy chuyển các tham số thông qua GET / POST thông thường với một tên khác và để mã phía máy chủ của bạn xử lý nó.

Tôi đã gặp sự cố tương tự với proxy của riêng mình để bỏ qua CORS và tôi gặp phải lỗi POST-> OPTION tương tự trong Chrome. Đó là Authorizationtiêu đề trong trường hợp của tôi ( "x-li-format""X-UserName"đây là trường hợp của bạn.) Tôi đã chuyển nó ở định dạng giả (ví dụ: AuthorizatinJacktrong GET) và tôi đã thay đổi mã cho proxy của mình để biến nó thành tiêu đề khi thực hiện cuộc gọi đến đích . Đây là trong PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}

1

Trong trường hợp của tôi, tôi đang gọi một API được lưu trữ bởi AWS (API Gateway). Đã xảy ra lỗi khi tôi cố gắng gọi API từ một miền khác với miền của chính API. Vì tôi là chủ sở hữu API, tôi đã bật CORS cho môi trường thử nghiệm, như được mô tả trong Tài liệu Amazon .

Trong sản xuất, lỗi này sẽ không xảy ra, vì request và api sẽ nằm trong cùng một miền.

Tôi hy vọng nó sẽ giúp!


0

Như được trả lời bởi @Dark Falcon, tôi chỉ đơn giản là xử lý nó .

Trong trường hợp của tôi, tôi đang sử dụng máy chủ node.js và tạo một phiên nếu nó không tồn tại. Vì phương thức OPTIONS không có chi tiết phiên trong đó, nên nó đã tạo ra một phiên mới cho mọi yêu cầu phương thức POST.

Vì vậy, trong quy trình ứng dụng của tôi để tạo-phiên-nếu-không-tồn tại, tôi chỉ cần thêm một kiểm tra để xem có phương pháp không OPTIONSvà nếu có, chỉ cần bỏ qua phần tạo phiên:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }


0

Cân nhắc sử dụng axios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

Tôi đã gặp sự cố này bằng cách sử dụng tìm nạp và axios hoạt động hoàn hảo.


5
Axios cũng sử dụng tùy chọn đầu tiên
Skylin R

0

Tôi đã gặp một vấn đề tương tự. Tôi đã dành gần nửa ngày để hiểu tại sao mọi thứ hoạt động bình thường trong Firefox và không thành công trong Chrome. Trong trường hợp của tôi, đó là do các trường trùng lặp (hoặc có thể bị gõ nhầm) trong tiêu đề yêu cầu của tôi.


0

Sử dụng tìm nạp thay vì XHR, khi đó yêu cầu sẽ không được đánh dấu trước ngay cả khi nó được đặt chéo.


-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

the contentType: 'text / trơn; charset = utf-8 ', hoặc chỉ contentType:' text / trơn ', phù hợp với tôi! Trân trọng!!


Điều này có liên quan gì đến câu hỏi?
Corey Ogburn

Xin chào, tôi nghĩ điều này giải quyết được vấn đề trong tiêu đề, với loại nội dung này, bạn vượt qua phương thức OPTIONS. Trân trọng
David Lopes

ContentType không liên quan gì đến phương thức này.
Corey Ogburn

Tôi biết bạn đang nói gì, nhưng hãy thử. tùy thuộc vào trình duyệt, loại nội dung của bạn có thể ảnh hưởng đến yêu cầu của bạn và thay đổi Phương pháp của bạn!
David Lopes
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.