Как заставить работать пост-запрос совместного использования ресурсов между источниками (CORS)

171

У меня есть машина на моем локальном компьютере (machineA) с двумя веб-серверами. Первый - встроенный в XBMC (порт 8080) и отображает нашу библиотеку. Второй сервер - это python CherryPy script (порт 8081), который я использую для запуска преобразования файлов по требованию. Преобразование файла инициируется запросом AJAX POST со страницы, обслуживаемой сервером XBMC.

  • Перейти к http://machineA:8080, который отображает библиотеку
  • Отображается библиотека
  • Пользователь нажимает на ссылку "convert", которая выдает следующую команду:

Запрос 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
  • Сервер отвечает следующим образом:

Заголовок ответа - ОПЦИИ (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: *)

Для устранения неполадок я также выдал ту же команду $.post из http://jquery.com. Вот где я в тупике, от 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

Заголовок ответа - ОПЦИИ (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

Заголовок запроса - 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 (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

Я не могу понять, почему один и тот же запрос будет работать с одного сайта, но не с другим. Я надеюсь, что кто-то сможет указать, что мне не хватает. Спасибо за вашу помощь!

  • 0
    Нужен ли CORS, если оба веб-сервера находятся на одном компьютере?
  • 7
    Насколько мне известно, это запрос CORS из-за другого порта. Кроме того, запрос OPTIONS указывает, что браузер обрабатывает его как запрос CORS.
Показать ещё 1 комментарий
Теги:
cors
cherrypy

10 ответов

128
Лучший ответ

Я, наконец, наткнулся на эту ссылку Запрос 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 
57

ПРОСЬБА:

 $.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
  • 22
    jsonp не работает для POST-запроса
9

Пришло время найти решение.

В случае, если ваш ответ сервера правильно и запрос является проблемой, вы должны добавить 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 требуется

  • 0
    JQuery версия хорошо? Вы установили withCredentials: true ? Вы уверены, что у вас есть соответствующие заголовки?
  • 0
    Да, 1wCredential: true , версия jquery: 3.2.1`. На самом деле он работает через почтальона, но не проходит через браузер Chrome
Показать ещё 3 комментария
8

Хорошо, я боролся с этой проблемой в течение пары недель.

Самый простой, наиболее совместимый и не хакерский способ сделать это - это, вероятно, использовать JavaScript-провайдер провайдера, который не выполняет вызовы на основе браузера и может обрабатывать запросы Cross Origin.

например. API Facebook JavaScript и API Google JS.

В случае, если ваш поставщик API не является текущим и не поддерживает заголовок Origins Origins '*' в своем ответе и не имеет JS api (да, я говорю о вас Yahoo), вас поражает одна из трех параметры -

  • Использование jsonp в ваших запросах, которое добавляет функцию обратного вызова к вашему URL, где вы можете обрабатывать свой ответ. Предостережение это изменит URL-адрес запроса, чтобы ваш сервер API был оборудован для обработки обратного вызова? В конце URL-адреса.

  • Отправьте запрос на ваш сервер API, который является вашим контроллером, и находится либо в том же домене, что и клиент, либо включен перекрестный источник ресурсов, откуда вы можете проксировать запрос на сторонний сервер API.

  • Вероятно, наиболее полезно в тех случаях, когда вы делаете запросы OAuth и должны обрабатывать взаимодействие с пользователем. Ха-ха! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')

3

Использование этого в сочетании с Laravel решило мою проблему. Просто добавьте этот заголовок в свой запрос jQuery Access-Control-Request-Headers: x-requested-with и убедитесь, что ваш ответ на стороне сервера имеет этот набор заголовков Access-Control-Allow-Headers: *.

  • 4
    Нет причин добавлять заголовки CORS к запросу вручную. Браузер всегда добавляет заголовки prop CORS к вашему запросу.
  • 0
    Реальная проблема состоит в том, чтобы заставить сервер ответить с правильными Access-Control-Allow-Headers и JQ, предоставляющими правильные Access-Control-Allow-Headers Access-Control-Request-Headers (плюс любые, которые вы добавляете через код), ни один из которых не может быть подстановочными знаками. требуется только один «плохой» заголовок, чтобы взорвать предполетный полет, например, используя If-None-Match для условного GET, если на сервере его нет в списке.
2

Я решил свою собственную проблему при использовании API-интерфейсов google distance, установив свой заголовок запроса с помощью Jquery ajax. посмотрите ниже.

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":"*"
      }

**
Надеюсь, это поможет.

  • 0
    это единственное решение, сработавшее для нас без каких-либо изменений на стороне сервера ... спасибо miracool
  • 1
    @ Тимста, я рад, что мое предложение сработало для тебя. Благодарен также и за переполнение стека. Хорошего дня.
1

У меня была одна и та же проблема, когда jquery ajax только дал мне проблемы с коревыми вопросами в почтовых запросах, где запросы выполнялись нормально - я устал от всего выше, без каких-либо результатов. У меня были правильные заголовки на моем сервере и т.д. Переключение на использование 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)

Надеюсь, это поможет кому-то еще с теми же проблемами.

1

По какой-то причине вопрос о запросах GET был объединен с этим, поэтому я отвечу на него здесь.

Эта простая функция будет асинхронно получать ответ HTTP-статуса с страницы с поддержкой CORS. Если вы запустите его, вы увидите, что только страница с соответствующими заголовками возвращает статус 200 при доступе через XMLHttpRequest - используется ли GET или POST. Ничто не может быть сделано на стороне клиента, чтобы обойти это, за исключением, возможно, использования JSONP, если вам просто нужен json-объект.

Следующие сведения могут быть легко изменены для получения данных, хранящихся в объекте 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

Это резюме того, что сработало для меня:

Определите новую функцию (упростите $.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);

Это единственный способ узнать правду POST кросс-домена от JS.

JSONP преобразует POST в GET, который может отображать конфиденциальную информацию в журналах сервера.

-4

Это немного поздно для вечеринки, но я боролся с этим пару дней. Это возможно, и ни один из ответов, которые я нашел здесь, не работал. Это обманчиво просто. Здесь вызов .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='[email protected]'",
        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 скрипт не должен его устанавливать.
  • 0
    Hmmph. Это возродило мои надежды, а затем разбило их, когда я увидел, что вы делаете AJAX «GET» в своем коде, когда ОП совершенно ясно сказал, что пытается ИЗБЕГАТЬ, используя «GET», и хотел использовать «POST».
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню