关于jquery:如何获得跨源站资源共享(cors)后请求工作

How to get a cross-origin resource sharing (CORS) post request working

我的局域网上有一台机器,它有两个Web服务器。第一个是在XBMC(端口8080)中内置的一个,并显示我们的库。第二个服务器是一个ChelypyyPython脚本(端口8081),我用它来触发一个按需的文件转换。文件转换是由来自XBMC服务器的页面的Ajax POST请求触发的。

  • Goto http://MeMeCe: 8080显示库
  • 显示库
  • 用户单击"convert"链接,发出以下命令-

jquery-ajax请求

1
$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • 浏览器发出一个HTTP选项请求,其标题如下:

请求头-选项

1
2
3
4
5
6
7
8
9
10
11
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)

1
2
3
4
5
6
7
8
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
  • 然后对话停止。从理论上讲,当服务器以正确的(?)响应时,浏览器应该发出一个POST请求。CORS头文件(访问控制允许源文件:*)

为了进行故障排除,我还从http://jquery.com发出了相同的$.post命令。这就是我遇到困难的地方,从jquery.com,post请求起作用,然后通过post发送选项请求。此事务的标题如下:

请求头-选项

1
2
3
4
5
6
7
8
9
10
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)

1
2
3
4
5
6
7
8
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

请求头-发布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

响应头-日志(状态=200 OK)

1
2
3
4
5
6
7
8
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

我不明白为什么同一个请求在一个站点上有效,而在另一个站点上无效。我希望有人能指出我遗漏了什么。谢谢你的帮助!


我终于偶然发现了这个链接:"CORS POST请求是从普通JavaScript中工作的,但是为什么不使用jQuery呢?"注意到jQuery 1.5.1增加了

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

头向所有CORS请求。jQuery 1.5.2不这样做。同样,根据同一个问题,设置服务器响应头

1
Access-Control-Allow-Headers: *

不允许响应继续。您需要确保响应头具体包含所需的报头。IE:

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

请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 $.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");
            }
        });

回应:

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

return response


我花了点时间才找到解决办法。

如果您的服务器响应正确,并且请求有问题,您应该在请求中添加withCredentials: truexhrFields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.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
    }
});

Note: jQuery >= 1.5.1 is required


我在这个问题上挣扎了几个星期。

要做到这一点,最简单、最兼容和非黑客的方法可能是使用一个提供程序JavaScriptAPI,它不进行基于浏览器的调用,并且可以处理跨源请求。

例如facebook javascript api和google js api。

如果您的API提供程序不是最新的,并且在其响应中不支持跨源资源源'*'头,并且没有JSAPI(是的,我是在说您Yahoo),您会被三个选项中的一个击中。-

  • 在请求中使用JSONP,这会向URL添加一个回调函数,您可以在其中处理响应。警告:这将更改请求的URL,因此您的API服务器必须具备处理?callback=在URL的末尾。

  • 将请求发送到您的API服务器,该服务器是您的控制器,与客户端位于同一域中,或者启用了跨源资源共享,从中可以将请求代理到第三方API服务器。

  • 在您发出OAuth请求并需要处理用户交互的情况下,可能最有用,哈哈!window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')


  • 我用jqueryajax设置了我的请求头,解决了我在使用谷歌距离矩阵API时遇到的问题。看看下面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);

          });

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

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

    **我希望这有帮助。


    把这个和拉拉维尔结合起来解决了我的问题。只需将这个头添加到jquery请求Access-Control-Request-Headers: x-requested-with中,并确保服务器端响应设置了这个头Access-Control-Allow-Headers: *


    我有一个完全相同的问题,jQueryAjax只给了我关于POST请求的CORS问题,GET请求工作得很好——我厌倦了上面的所有工作,没有结果。我的服务器等中有正确的头文件。转换为使用xmlhttprequest而不是jquery立即解决了我的问题。不管我使用哪个版本的jquery,它都没有修复它。如果不需要向后的浏览器兼容性,FETCH也不会有问题。

    1
    2
    3
    4
    5
    6
    7
    8
            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)

    希望这有助于解决同样问题的其他人。


    出于某种原因,关于GET请求的问题已与此问题合并,因此我将在此处对此作出响应。

    这个简单的函数将从启用CORS的页面异步获取HTTP状态回复。如果您运行它,您将看到只有具有适当头的页面在通过xmlhttprequest访问时返回200状态——无论使用GET还是POST。除了可能使用JSONP(如果您只需要一个JSON对象)之外,在客户机端什么也做不到。

    可以轻松修改以下内容,以获取保存在xmlhttpRequestObject对象中的数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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");
        }
      }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <html>
    <head>
      Check if page is cors
    </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>


    如果由于某些原因,在尝试添加头文件或设置控制策略时,仍然没有找到可以考虑使用ApacheProxyPass的地方…

    例如,在一个使用ssl的中,添加以下两个指令:

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

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

    • 代理
    • 脯氨酸连接
    • PROXY-HTTP

    显然,为了使用Apache代理,您必须更改Ajax请求的URL…


    以下是我的工作总结:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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({})
        }
      });
    }

    用途:

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

    还与.done.fail等合作:

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

    服务器端(本例中是example.com的宿主),设置这些头(在php中添加了一些示例代码):

    1
    2
    3
    4
    5
    6
    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跨域发布的方法。

    jsonp将post转换为get,get可以在服务器日志中显示敏感信息。


    这次聚会有点晚了,但我已经为此挣扎了几天了。这是可能的,我在这里找到的答案都没有奏效。这看起来很简单。以下是.ajax调用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        <!DOCTYPE HTML>
        <html>
        <head>
        <body>
         Javascript Test
         <script src="http://code.jquery.com/jquery-latest.min.js">
         <script type="text/javascript">
         $(document).domain = 'XXX.com';
         $(document).ready(function () {
         $.ajax({
            xhrFields: {cors: false},
            type:"GET",
            url:"http://XXXX.com/test.php?email='[email protected]'",
            success: function (data) {
               alert(data);
            },
            error: function (x, y, z) {
               alert(x.responseText +" :EEE:" + x.status);
            }
        });
        });
         
        </body>
        </html>

    下面是服务器端的PHP:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
        <html>
        <head>
         PHP Test
         </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"] . "";
          }
        } else {
          echo"{ }";
        }
        $conn->close();
        ?>
        </body>