首页 文章

如何使跨域资源共享(CORS)发布请求正常工作

提问于
浏览
176

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

  • 转到http://machineA:8080,显示库

  • 显示库

  • 用户点击发出以下命令的'convert'链接 -

jQuery Ajax请求

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

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

Response Header - OPTIONS (STATUS = 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
  • 然后会话停止 . 理论上,浏览器应该发出POST请求,因为服务器使用正确的(?)CORS头响应(Access-Control-Allow-Origin:*)

为了排除故障,我还从http://jquery.com发出了相同的$ .post命令 . 这是我难倒的地方,从jquery.com,邮件请求工作,OPTIONS请求后发送一个POST . 此交易的 Headers 如下;

Request Header - 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://jquery.com
Access-Control-Request-Method: POST

Response Header - OPTIONS (STATUS = 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

Request Header - 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

Response Header - 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

我无法弄清楚为什么同一个请求可以在一个站点上运行,而不是另一个站点 . 我希望有人能够指出我所缺少的东西 . 谢谢你的帮助!

10 回答

  • 134

    我终于偶然发现了这个链接“A CORS POST request works from plain javascript, but why not with 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
    
  • 1

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

  • 3

    这是对我有用的总结:

    定义一个新函数(包装 $.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,这可能会在服务器日志中显示敏感信息 .

  • 8

    这对派对来说有点晚了,但我几天都在苦苦挣扎 . 这是可能的,我在这里找到的答案都没有奏效 . 这看起来很简单 . 这是.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>
    
  • 56

    请求:

    $.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
    
  • 1

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

    如果您的服务器响应正确且请求有问题,您应该将 withCredentials: true 添加到请求中的 xhrFields

    $.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是必需的

  • 10

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

    最简单,最顺从和非hacky的方法是使用提供程序JavaScript API,它不会进行基于浏览器的调用,并且可以处理Cross Origin请求 .

    例如 . Facebook JavaScript API和Google JS API .

    如果您的API提供程序不是最新的,并且在其响应中不支持Cross Origin Resource Origin'*'标头,并且没有JS api(是的,我在谈论您的Yahoo),您会被三个选项中的一个选中 -

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

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

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

  • -4

    出于某种原因,关于GET请求的问题已与此合并,因此我将在此处回复 .

    这个简单的函数将异步从启用CORS的页面获取HTTP状态回复 . 如果您运行它,您将看到只有具有正确标头的页面返回200状态,如果通过XMLHttpRequest访问 - 无论是使用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>
    
  • 0

    我有完全相同的问题,jquery ajax只给了我关于获取请求工作正常的帖子请求的cors问题 - 我累了上面的所有内容没有结果 . 我有我的服务器等正确的 Headers . 更改为使用XMLHTTPRequest而不是jquery立即修复我的问题 . 无论我使用哪个版本的jquery都没有解决它 . 如果您不需要向后兼容浏览器,则Fetch也可以正常运行 .

    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)
    

    希望这可以帮助其他人解决同样的问题 .

  • 4

    通过使用Jquery ajax设置我的请求标头,我在使用谷歌距离矩阵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":"*"
          }
    

    **
    我希望这有帮助 .

相关问题