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

217

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

  • Перейти к http: // machineA: 8080, который отображает библиотеку
  • Библиотека отображается
  • Пользователь нажимает на ссылку «конвертировать», которая выполняет следующую команду -

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 ОК)

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

Заголовок ответа - ОПЦИИ (СОСТОЯНИЕ = 200 ОК)

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

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

Джеймс
источник
Нужен ли CORS, если оба веб-сервера находятся на одном компьютере?
jdigital
8
Насколько мне известно, это запрос CORS из-за другого порта. Кроме того, запрос OPTIONS указывает, что браузер обрабатывает его как запрос CORS
Джеймс

Ответы:

158

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

Я решил свою проблему при использовании API матрицы расстояний Google, установив заголовок запроса с помощью 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":"*"
      }

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

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

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

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

Dekel
источник
JQuery версия хорошо? Вы установили withCredentials: true? Вы уверены, что у вас есть соответствующие заголовки?
Декел
Да, 1withCredential: true , версия jquery: 3.2.1`. На самом деле он работает через почтальона, но не проходит через браузер Chrome
Mox Shah
1
Я почти уверен, что у почтальона не должно быть проблем с CORS, потому что это не браузер и его поведение отличается. Вы уверены, что у вас есть правильные и релевантные заголовки, отправленные с сервера клиенту? Опять же - обратите внимание , что это изменение не достаточно. Вы должны убедиться, что сервер отвечает с правильными заголовками.
Декел
Не могли бы вы сказать мне, какой заголовок ответа требуется на сервере?
Мокс Шах
@MoxShah это совершенно иной вопрос , и есть много ресурсов там :)
Dekel
9

Ну, я боролся с этой проблемой в течение нескольких недель.

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

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

Если ваш провайдер API не является текущим и не поддерживает заголовок Cross Origin Resource Origin '*' в своем ответе и не имеет API JS (да, я говорю о вас, Yahoo), вы получаете один из трех вариантов:

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

  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: *.

MK
источник
6
Нет причин добавлять заголовки CORS к запросу вручную. Браузер всегда добавляет заголовки prop CORS к вашему запросу.
Рэй Николус
1
Реальная проблема состоит в том, чтобы заставить сервер ответить с правильным Access-Control-Allow-Headersи JQ поставляет правильные Access-Control-Request-Headers(плюс все, что вы добавляете через код), ни один из которых не может быть подстановочными знаками. требуется только один «плохой» заголовок, чтобы взорвать предполетный полет, например, использовать If-None-Matchдля условного GET, если на сервере его нет в списке.
escape-ООО
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

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

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

ozzieisaacs
источник
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, который может отображать конфиденциальную информацию в журналах сервера.

Лепе
источник
0

Если по каким-то причинам, пытаясь добавить заголовки или установить политику управления, вы все еще не получаете ничего, вы можете подумать об использовании apache ProxyPass…

Например, в том, <VirtualHost>который использует SSL, добавьте две следующие директивы:

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

Убедитесь, что следующие модули Apache загружены (загрузите их с помощью a2enmod):

  • полномочие
  • proxy_connect
  • proxy_http

Очевидно, вам придется изменить URL-адрес ваших запросов AJAX, чтобы использовать прокси-сервер apache…

llange
источник
-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 скрипт не должен его устанавливать.
Лапша
Hmmph. Это возродило мои надежды, а затем разбило их, когда я увидел, что вы делаете AJAX «GET» в своем коде, когда ОП совершенно ясно сказал, что он пытается ИЗБЕГАТЬ, используя «GET», и хотел использовать «POST».
Стив
Заголовки CORS работают одинаково независимо от глагола. Мы можем вызывать все глаголы через $.ajax()правильно настроенный сервер. Самое сложное - получить Access-Control-Request-Headersправильное, но даже это не так уж сложно. Как отмечалось в предыдущих постерах, это должен быть не шаблон, а белый список заголовков.
побег-ООО