Làm thế nào để yêu cầu bài viết chia sẻ tài nguyên nguồn gốc (CORS) hoạt động


216

Tôi có một máy trên lan cục bộ (machineA) có hai máy chủ web. Đầu tiên là bản dựng sẵn trong XBMC (trên cổng 8080) và hiển thị thư viện của chúng tôi. Máy chủ thứ hai là tập lệnh python CherryPy (cổng 8081) mà tôi đang sử dụng để kích hoạt chuyển đổi tệp theo yêu cầu. Việc chuyển đổi tệp được kích hoạt bởi yêu cầu AJAX POST từ trang được cung cấp từ máy chủ XBMC.

  • Goto http: // machineA: 8080 hiển thị thư viện
  • Thư viện được hiển thị
  • Người dùng nhấp vào liên kết 'convert', đưa ra lệnh sau -

Yêu cầu jQuery jQuery

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • Trình duyệt đưa ra yêu cầu HTTP TÙY CHỌN với các tiêu đề sau;

Tiêu đề yêu cầu - TÙY CHỌN

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://machineA:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with
  • Máy chủ trả lời như sau;

Tiêu đề phản hồi - TÙY CHỌN (TÌNH TRẠNG = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:40:29 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1
  • Cuộc trò chuyện sau đó dừng lại. Về lý thuyết, trình duyệt sẽ đưa ra yêu cầu POST khi máy chủ trả lời với các tiêu đề CORS (?) Chính xác (Access-Control-Allow-Origin: *)

Để khắc phục sự cố, tôi cũng đã ban hành lệnh $ .post tương tự từ http://jquery.com . Đây là nơi tôi bị bối rối, từ jquery.com, yêu cầu bài viết hoạt động, một yêu cầu TÙY CHỌN được gửi theo sau bởi POST. Các tiêu đề từ giao dịch này là dưới đây;

Tiêu đề yêu cầu - TÙY CHỌN

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://jquery.com
Access-Control-Request-Method: POST

Tiêu đề phản hồi - TÙY CHỌN (TÌNH TRẠNG = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1

Tiêu đề yêu cầu - POST

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://jquery.com/
Content-Length: 12
Origin: http://jquery.com
Pragma: no-cache
Cache-Control: no-cache

Tiêu đề phản hồi - POST (STATUS = 200 OK)

Content-Length: 32
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: application/json

Tôi không thể tìm ra lý do tại sao cùng một yêu cầu sẽ hoạt động từ một trang web, nhưng không phải là một trang web khác. Tôi hy vọng ai đó có thể chỉ ra những gì tôi đang thiếu. Cảm ơn bạn đã giúp đỡ!


CORS có cần thiết nếu cả hai máy chủ web nằm trên cùng một máy không?
jdigital

8
Theo hiểu biết tốt nhất của tôi, đó là một yêu cầu CORS vì cổng khác nhau. Ngoài ra, yêu cầu TÙY CHỌN chỉ ra rằng trình duyệt đang coi nó là yêu cầu CORS
James

Câu trả lời:


157

Cuối cùng tôi cũng vấp phải liên kết này " Một yêu cầu CORS POST hoạt động từ javascript đơn giản, nhưng tại sao không phải với jQuery? " Lưu ý rằng jQuery 1.5.1 thêm vào

 Access-Control-Request-Headers: x-requested-with

tiêu đề cho tất cả các yêu cầu CORS. jQuery 1.5.2 không làm điều này. Ngoài ra, theo cùng một câu hỏi, đặt tiêu đề phản hồi của máy chủ là

Access-Control-Allow-Headers: *

không cho phép tiếp tục trả lời. Bạn cần đảm bảo tiêu đề phản hồi cụ thể bao gồm các tiêu đề cần thiết. I E:

Access-Control-Allow-Headers: x-requested-with 

70

YÊU CẦU:

 $.ajax({
            url: "http://localhost:8079/students/add/",
            type: "POST",
            crossDomain: true,
            data: JSON.stringify(somejson),
            dataType: "json",
            success: function (response) {
                var resp = JSON.parse(response)
                alert(resp.status);
            },
            error: function (xhr, status) {
                alert("error");
            }
        });

PHẢN ỨNG:

response = HttpResponse(json.dumps('{"status" : "success"}'))
response.__setitem__("Content-type", "application/json")
response.__setitem__("Access-Control-Allow-Origin", "*")

return response

3
nếu máy chủ không chấp nhận nguồn gốc chéo, crossdomain = true không phải giải quyết vấn đề. sử dụng dataType: "jsonp" và thiết lập cuộc gọi lại như jsonpCallback: "hồi đáp" sẽ là ý tưởng tốt hơn để làm điều này. Xem thêm: api.jquery.com/jquery.ajax
BonifatiusK

14

Tôi đã giải quyết vấn đề của riêng mình khi sử dụng API ma trận khoảng cách google bằng cách đặt tiêu đề yêu cầu của tôi với Jquery ajax. hãy xem bên dưới

var settings = {
          'cache': false,
          'dataType': "jsonp",
          "async": true,
          "crossDomain": true,
          "url": "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=place_id:"+me.originPlaceId+"&destinations=place_id:"+me.destinationPlaceId+"&region=ng&units=metric&key=mykey",
          "method": "GET",
          "headers": {
              "accept": "application/json",
              "Access-Control-Allow-Origin":"*"
          }
      }

      $.ajax(settings).done(function (response) {
          console.log(response);

      });

Lưu ý những gì tôi đã thêm vào cài đặt
**

"headers": {
          "accept": "application/json",
          "Access-Control-Allow-Origin":"*"
      }

**
Tôi hy vọng điều này sẽ giúp.


đây là giải pháp duy nhất hiệu quả với chúng tôi mà không thay đổi bất cứ điều gì ở phía máy chủ ... thanx miracool
Timsta

1
@Timsta, tôi rất vui vì đề xuất của tôi đã làm việc cho bạn. Biết ơn để chồng tràn cũng. Có một ngày tuyệt vời.
Miracool

13

Mất một thời gian để tìm giải pháp.

Trong trường hợp máy chủ của bạn phản hồi chính xác và yêu cầu là vấn đề, bạn nên thêm withCredentials: truevào xhrFieldstrong yêu cầu:

$.ajax({
    url: url,
    type: method,
    // This is the important part
    xhrFields: {
        withCredentials: true
    },
    // This is the important part
    data: data,
    success: function (response) {
        // handle the response
    },
    error: function (xhr, status) {
        // handle errors
    }
});

Lưu ý: jQuery> = 1.5.1 là bắt buộc


Phiên bản jquery ok? bạn đã thiết lập withCredentials: true? Bạn có chắc là bạn có các tiêu đề liên quan?
Dekel

Có, 1withCredential: , phiên bản jquery đúng : 3.2.1`. Trên thực tế, nó đang hoạt động thông qua người đưa thư, nhưng nó không chuyển qua trình duyệt chrome
Mox Shah

1
Tôi gần như chắc chắn người đưa thư không nên gặp sự cố CORS vì đó không phải là trình duyệt và hành vi của nó cũng khác. Bạn có chắc chắn rằng bạn có các tiêu đề chính xác và có liên quan gửi từ máy chủ đến máy khách không? Một lần nữa - lưu ý rằng thay đổi này là không đủ. Bạn cần đảm bảo phản hồi của máy chủ với các tiêu đề chính xác .
Dekel

Bạn có thể cho tôi biết tiêu đề phản hồi được yêu cầu trên máy chủ là gì?
Mox Shah

@MoxShah đây là một câu hỏi hoàn toàn khác và có rất nhiều tài nguyên ở đó :)
Dekel

9

Vâng, tôi đã đấu tranh với vấn đề này trong một vài tuần.

Cách dễ nhất, phù hợp nhất và không hack để làm điều này là có thể sử dụng API JavaScript của nhà cung cấp không thực hiện các cuộc gọi dựa trên trình duyệt và có thể xử lý các yêu cầu Cross Origin.

Ví dụ: API JavaScript của Facebook và API Google JS.

Trong trường hợp nhà cung cấp API của bạn không hiện tại và không hỗ trợ tiêu đề Cross * Resource Origin '' 'trong phản hồi của nó và không có api JS (Có tôi đang nói về bạn Yahoo), bạn sẽ gặp phải một trong ba tùy chọn-

  1. Sử dụng jsonp trong các yêu cầu của bạn để thêm chức năng gọi lại vào URL của bạn, nơi bạn có thể xử lý phản hồi của mình. Hãy cẩn thận điều này sẽ thay đổi URL yêu cầu để máy chủ API của bạn phải được trang bị để xử lý? Callback = ở cuối URL.

  2. Gửi yêu cầu đến máy chủ API của bạn, bộ điều khiển của bạn và nằm trong cùng miền với máy khách hoặc được bật Chia sẻ tài nguyên nguồn gốc từ nơi bạn có thể ủy quyền yêu cầu đến máy chủ API của bên thứ 3.

  3. Có lẽ hữu ích nhất trong trường hợp bạn đang thực hiện các yêu cầu OAuth và cần xử lý tương tác người dùng Haha! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')


4

Sử dụng kết hợp với Laravel đã giải quyết vấn đề của tôi. Chỉ cần thêm tiêu đề này vào yêu cầu jquery của bạn Access-Control-Request-Headers: x-requested-withvà đảm bảo rằng phản hồi phía máy chủ của bạn có bộ tiêu đề này Access-Control-Allow-Headers: *.


6
Không có lý do để thêm tiêu đề CORS vào yêu cầu theo cách thủ công. Trình duyệt sẽ luôn thêm các tiêu đề CORS prop theo yêu cầu cho bạn.
Ray Nicholus

1
Thách thức thực sự là làm cho máy chủ trả lời đúng Access-Control-Allow-Headersvà JQ cung cấp đúng Access-Control-Request-Headers(cộng với bất kỳ bạn thêm qua mã) không phải cái nào cũng có thể là ký tự đại diện. nó chỉ mất một tiêu đề "xấu" để làm nổ tung chuyến bay trước, ví dụ như sử dụng If-None-Matchcho một GET có điều kiện, nếu máy chủ không có danh sách đó.
esc-llc

1

Vì một số lý do, một câu hỏi về các yêu cầu GET đã được hợp nhất với câu hỏi này, vì vậy tôi sẽ trả lời nó ở đây.

Hàm đơn giản này sẽ nhận được phản hồi trạng thái HTTP không đồng bộ từ trang hỗ trợ CORS. Nếu bạn chạy nó, bạn sẽ thấy rằng chỉ một trang có tiêu đề phù hợp sẽ trả về 200 trạng thái nếu được truy cập qua XMLHttpRequest - cho dù GET hoặc POST được sử dụng. Không có gì có thể được thực hiện ở phía máy khách để khắc phục điều này ngoại trừ có thể sử dụng JSONP nếu bạn chỉ cần một đối tượng json.

Sau đây có thể dễ dàng sửa đổi để có được dữ liệu được giữ trong đối tượng xmlHttpRequestObject:

function checkCorsSource(source) {
  var xmlHttpRequestObject;
  if (window.XMLHttpRequest) {
    xmlHttpRequestObject = new XMLHttpRequest();
    if (xmlHttpRequestObject != null) {
      var sUrl = "";
      if (source == "google") {
        var sUrl = "https://www.google.com";
      } else {
        var sUrl = "https://httpbin.org/get";
      }
      document.getElementById("txt1").innerHTML = "Request Sent...";
      xmlHttpRequestObject.open("GET", sUrl, true);
      xmlHttpRequestObject.onreadystatechange = function() {
        if (xmlHttpRequestObject.readyState == 4 && xmlHttpRequestObject.status == 200) {
          document.getElementById("txt1").innerHTML = "200 Response received!";
        } else {
          document.getElementById("txt1").innerHTML = "200 Response failed!";
        }
      }
      xmlHttpRequestObject.send();
    } else {
      window.alert("Error creating XmlHttpRequest object. Client is not CORS enabled");
    }
  }
}
<html>
<head>
  <title>Check if page is cors</title>
</head>
<body>
  <p>A CORS-enabled source has one of the following HTTP headers:</p>
  <ul>
    <li>Access-Control-Allow-Headers: *</li>
    <li>Access-Control-Allow-Headers: x-requested-with</li>
  </ul>
  <p>Click a button to see if the page allows CORS</p>
  <form name="form1" action="" method="get">
    <input type="button" name="btn1" value="Check Google Page" onClick="checkCorsSource('google')">
    <input type="button" name="btn1" value="Check Cors Page" onClick="checkCorsSource('cors')">
  </form>
  <p id="txt1" />
</body>
</html>


1

Tôi đã có cùng một vấn đề chính xác khi jquery ajax chỉ cung cấp cho tôi các vấn đề về các yêu cầu bài đăng trong đó các yêu cầu hoạt động tốt - tôi mệt mỏi với mọi thứ ở trên mà không có kết quả. Tôi đã có các tiêu đề chính xác trong máy chủ của mình, v.v. Thay đổi để sử dụng XMLHTTPRequest thay vì jquery đã khắc phục sự cố của tôi ngay lập tức. Cho dù tôi sử dụng phiên bản jquery nào thì nó cũng không sửa được. Tìm nạp cũng hoạt động mà không có vấn đề nếu bạn không cần khả năng tương thích trình duyệt lạc hậu.

        var xhr = new XMLHttpRequest()
        xhr.open('POST', 'https://mywebsite.com', true)
        xhr.withCredentials = true
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 2) {// do something}
        }
        xhr.setRequestHeader('Content-Type', 'application/json')
        xhr.send(json)

Hy vọng điều này sẽ giúp bất cứ ai khác có cùng vấn đề.


1

Đây là một bản tóm tắt về những gì làm việc cho tôi:

Xác định một chức năng mới (được gói $.ajaxđể đơn giản hóa):

jQuery.postCORS = function(url, data, func) {
  if(func == undefined) func = function(){};
  return $.ajax({
    type: 'POST', 
    url: url, 
    data: data, 
    dataType: 'json', 
    contentType: 'application/x-www-form-urlencoded', 
    xhrFields: { withCredentials: true }, 
    success: function(res) { func(res) }, 
    error: function() { 
            func({}) 
    }
  });
}

Sử dụng:

$.postCORS("https://example.com/service.json",{ x : 1 },function(obj){
      if(obj.ok) {
           ...
      }
});

Cũng làm việc với .done, .fail, vv:

$.postCORS("https://example.com/service.json",{ x : 1 }).done(function(obj){
      if(obj.ok) {
           ...
      }
}).fail(function(){
    alert("Error!");
});

Phía máy chủ (trong trường hợp này là example.com được lưu trữ), hãy đặt các tiêu đề này (đã thêm một số mã mẫu trong PHP):

header('Access-Control-Allow-Origin: https://not-example.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 604800');
header("Content-type: application/json");
$array = array("ok" => $_POST["x"]);
echo json_encode($array);

Đây là cách duy nhất tôi biết để thực sự POST tên miền chéo từ JS.

JSONP chuyển đổi POST thành GET có thể hiển thị thông tin nhạy cảm tại nhật ký máy chủ.


0

Nếu vì một số lý do trong khi cố gắng thêm tiêu đề hoặc đặt chính sách kiểm soát, bạn vẫn không nhận được nơi nào bạn có thể xem xét sử dụng apache ProxyPasslahoma

Ví dụ: trong một <VirtualHost>sử dụng SSL, hãy thêm hai chỉ thị sau:

SSLProxyEngine On
ProxyPass /oauth https://remote.tld/oauth

Đảm bảo các mô-đun apache sau được tải (tải chúng bằng a2enmod):

  • Ủy quyền
  • proxy_connect
  • proxy_http

Rõ ràng là bạn sẽ phải thay đổi url yêu cầu AJAX của mình để sử dụng proxy apache apache


-3

Đây là một bữa tiệc muộn, nhưng tôi đã phải vật lộn với điều này trong một vài ngày. Có thể và không có câu trả lời tôi tìm thấy ở đây đã làm việc. Nó đơn giản một cách giả dối. Đây là cuộc gọi .ajax:

    <!DOCTYPE HTML>
    <html>
    <head>
    <body>
     <title>Javascript Test</title>
     <script src="http://code.jquery.com/jquery-latest.min.js"></script>
     <script type="text/javascript">
     $(document).domain = 'XXX.com';
     $(document).ready(function () {
     $.ajax({
        xhrFields: {cors: false},
        type: "GET",
        url: "http://XXXX.com/test.php?email='steve@XXX.com'",
        success: function (data) {
           alert(data);
        },
        error: function (x, y, z) {
           alert(x.responseText + " :EEE: " + x.status);
        }
    });
    });
    </script> 
    </body>
    </html>

Đây là php ở phía máy chủ:

    <html>
    <head>
     <title>PHP Test</title>
     </head>
    <body>
      <?php
      header('Origin: xxx.com');
      header('Access-Control-Allow-Origin:*');
      $servername = "sqlxxx";
      $username = "xxxx";
      $password = "sss";
      $conn = new mysqli($servername, $username, $password);
      if ($conn->connect_error) {
        die( "Connection failed: " . $conn->connect_error);
      }
      $sql = "SELECT email, status, userdata  FROM msi.usersLive";
      $result = $conn->query($sql);
      if ($result->num_rows > 0) {
      while($row = $result->fetch_assoc()) {
        echo $row["email"] . ":" . $row["status"] . ":" . $row["userdata"] .  "<br>";
      }
    } else {
      echo "{ }";
    }
    $conn->close();
    ?>
    </body>


1
Đối với giá trị của nó, tiêu đề Origin là tiêu đề yêu cầu, không phải tiêu đề phản hồi. Tập lệnh php của bạn không nên thiết lập nó.

Hừm. Điều này đã làm tôi hy vọng và sau đó đập tan chúng khi tôi thấy bạn đang tạo AJAX "GET" trong mã của mình, khi OP nói rõ rằng anh ta đang cố gắng TRÁNH bằng cách sử dụng "GET" và muốn sử dụng "POST".
Steve Sauder

Tiêu đề CORS hoạt động như nhau bất kể động từ liên quan. Chúng tôi có thể gọi tất cả các động từ thông qua $.ajax()một máy chủ được cấu hình đúng. Phần khó nhất là nhận được Access-Control-Request-Headerschính xác, nhưng ngay cả điều đó không quá khó. Theo ghi nhận của các áp phích trước, đây không phải là ký tự đại diện mà là danh sách trắng của các tiêu đề.
esc-llc
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.