如何获得跨域资源共享(CORS)的后请求工作


216

我在本地局域网(machineA)上有一台具有两个Web服务器的计算机。第一个是XBMC中的内置端口(在端口8080上),并显示我们的库。第二台服务器是我用来触发按需转换文件的CherryPy python脚本(端口8081)。由XBMC服务器提供的页面上的AJAX POST请求触发文件转换。

jQuery Ajax请求

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • 浏览器发出带有以下标头的HTTP OPTIONS请求;

请求标头-选项

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
  • 服务器响应以下内容;

响应标题-选项(状态= 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
  • 然后对话停止。理论上,浏览器应该在服务器以正确的(?)CORS标头响应时发出POST请求(Access-Control-Allow-Origin:*)

为了进行故障排除,我还从http://jquery.com发出了相同的$ .post命令。这是我在jquery.com遇到麻烦的地方,发布请求有效,OPTIONS请求由POST发送。该事务的标题在下面;

请求标头-选项

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

响应标题-选项(状态= 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

请求标头-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

响应标头-POST(状态= 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

我无法弄清楚为什么相同的请求会在一个站点上起作用,而在另一个站点上却不起作用。我希望有人能够指出我所缺少的。谢谢你的帮助!


如果两个Web服务器都在同一台计算机上,是否需要CORS?
jdigital 2011年

8
据我所知,由于端口不同,这是一个CORS请求。此外,OPTIONS请求还表明浏览器将其视为CORS请求
James

Answers:


157

我最终偶然发现了此链接“ CORS POST请求可从普通javascript运行,但为什么不使用jQuery? ”指出jQuery 1.5.1添加了

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

所有CORS请求的标头。jQuery 1.5.2不会这样做。另外,根据相同的问题,设置服务器响应标头为

Access-Control-Allow-Headers: *

不允许响应继续。您需要确保响应标头专门包含必需的标头。即:

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

70

请求:

 $.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");
            }
        });

响应:

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

return response

3
如果服务器不接受跨源,则crossdomain = true不必解决此问题。使用dataType:“ jsonp”并将回调设置为jsonpCallback:“ response”会是更好的主意。另请参阅:api.jquery.com/jquery.ajax
BonifatiusK,

14

通过使用Jquery ajax设置请求标头,我在使用Google距离矩阵API时解决了自己的问题。看看下面。

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);

      });

注意我在设置中添加的内容
**

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

**
我希望这会有所帮助。


这是对我们有效的唯一解决方案,无需在服务器端进行任何更改... thanx miracool
Timsta,

1
@Timsta,很高兴我的建议对您有用。也感谢堆栈溢出。祝你有美好的一天。
Miracool

13

花了我一些时间找到解决方案。

在正常情况下,您的服务器响应和请求的问题,您应该添加withCredentials: truexhrFields在请求:

$.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
    }
});

注意:jQuery> = 1.5.1是必需的


jQuery版本好吗?你设置了withCredentials: true吗?您确定您具有相关的标题吗?
Dekel

是的,1withCredential:正确的, jquery版本:3.2.1`。实际上,它正在通过邮递员进行工作,但未通过chrome浏览器进行工作
Mox Shah

1
我几乎可以肯定,邮递员应该不会遇到CORS问题,因为它不是浏览器,而且行为也有所不同。您确定从服务器发送到客户端的标题正确且相关吗?再次-请注意,此更改不够。您需要确保服务器响应具有正确的头。
Dekel

你能告诉我服务器上需要什么响应头吗?
Mox Shah

@MoxShah这是一个完全不同的问题,那里有很多资源:)
Dekel

9

好吧,我在这个问题上苦苦挣扎了几个星期。

最简单,最合规且最简单的方法是使用提供程序JavaScript API,该API不进行基于浏览器的调用,并且可以处理跨源请求。

例如Facebook JavaScript API和Google JS API。

如果您的API提供程序不是最新的,并且在响应中不支持跨源资源源'*'标头并且没有JS api(是的,我在谈论您Yahoo),那么您会被以下三个选项之一震惊:

  1. 在请求中使用jsonp,这会在URL中添加回调函数,您可以在其中处理响应。请注意,这将更改请求URL,因此您的API服务器必须具备处理URL末尾的?callback =的能力。

  2. 将请求发送到由您控制的API服务器,该服务器与客户端在同一个域中,或者已启用跨源资源共享,您可以在其中将请求代理到第三方API服务器。

  3. 在您发出OAuth请求并需要处理用户交互时,可能最有用。 window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')


4

与Laravel结合使用可以解决我的问题。只需将此标头添加到您的jquery请求中Access-Control-Request-Headers: x-requested-with,并确保服务器端响应已设置了此标头Access-Control-Allow-Headers: *


6
没有理由手动将CORS标头添加到请求中。浏览器将始终将prop CORS标头添加到您的请求中。
Ray Nicholus 2014年

1
真正的挑战是让服务器以正确的方式进行响应,Access-Control-Allow-Headers并且JQ提供正确的Access-Control-Request-Headers(以及通过代码添加的任何内容),都不是通配符。If-None-Match如果服务器没有列出该消息,则只需一个“错误”标头即可结束预检,例如用于条件GET。
escape-llc

1

由于某种原因,关于GET请求的问题已与该请求合并,因此我将在此处答复。

这个简单的函数将从启用CORS的页面异步获取HTTP状态回复。如果运行它,您将看到,只有通过XMLHttpRequest访问的带有正确标题的页面才能返回200状态-使用GET还是POST。如果只需要一个json对象,则除了使用JSONP之外,客户端上什么也无法解决。

可以轻松修改以下内容,以获取保存在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

我有一个完全相同的问题,即jquery ajax仅在发布请求中给我cors问题,而get请求却正常工作-我厌倦了上面的一切,但没有结果。我在服务器等中具有正确的标头。改用XMLHTTPRequest而不是jquery立即解决了我的问题。无论我使用哪个版本的jquery都无法修复它。如果不需要向后浏览器兼容性,则访存也不会出现问题。

        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)

希望这对其他遇到相同问题的人有所帮助。


1

这是对我有用的摘要:

定义一个新函数(包装$.ajax以简化):

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({}) 
    }
  });
}

用法:

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

也有工作.done.fail等:

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

服务器端(在本例中为example.com托管),设置以下标头(在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);

这是我知道真正从JS跨域发布POST的唯一方法。

JSONP将POST转换为GET,这可能会在服务器日志中显示敏感信息。


0

如果由于某些原因尝试添加标头或设置控制策略时,您仍然无所适从,则可以考虑使用apache ProxyPass…

例如,在<VirtualHost>使用SSL的示例中,添加以下两个指令:

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

确保已加载以下apache模块(使用a2enmod加载它们):

  • 代理
  • proxy_connect
  • proxy_http

显然,您必须更改AJAX请求url才能使用apache代理…


-3

这次聚会晚了一点,但是我已经为此挣扎了几天。有可能,我在这里找到的答案都没有奏效。这看似简单。这是.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>

这是服务器端的php:

    <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
就其价值而言,Origin标头是请求标头,而不是响应标头。您的php脚本不应该设置它。
面条

m 这激发了我的希望,然后当我看到您在代码中创建AJAX“ GET”时便破灭了,而OP明确表示他正试图使用​​“ GET”来避免并想使用“ POST”。
Steve Sauder

无论所涉及的动词如何,CORS标头的工作方式都相同。我们能够通过$.ajax()对正确配置的服务器调用所有动词。最难的部分是获得Access-Control-Request-Headers正确的方法,但是即使那样也不太困难。正如以前的张贴者所指出的那样,这一定不能是通配符,而是标头的白名单。
escape-llc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.