Як запрацювати запит на обмін ресурсами між походженнями (CORS)


216

У мене на локальному LAN (machineA) є машина, яка має два веб-сервери. Перший - це вбудований у XBMC (на порт 8080) і відображає нашу бібліотеку. Другий сервер - це сценарій python CherryPy (порт 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 ОК)

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
Джеймс

Відповіді:


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
якщо сервер не приймає кросове походження, кросдомен = true не повинен вирішити проблему. за допомогою dataType: "jsonp" та встановлення зворотного дзвінка, як jsonpCallback: "відповідь" було б кращою ідеєю для цього. Дивіться також: 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
Timsta

1
@Timsta, радий, моя пропозиція спрацювала для тебе. Вдячний також для укладання стікання. Гарного дня.
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 потрібно


версія jquery в порядку? Ви встановили withCredentials: true? ви впевнені, що маєте відповідні заголовки?
Декель

Так, 1withCredential: , версія вірного jquery: 3.2.1`. Насправді він працює через листоноші, але його не проходить через хромований браузер
Mox Shah

1
Я майже впевнений, що у листоноші не повинно виникнути проблем із CORS, оскільки це не веб-переглядач, а його поведінка відрізняється. Ви впевнені, що правильні та відповідні заголовки надсилають із сервера до клієнта? Знову ж - зверніть увагу , що ця зміна НЕ достатньо. Потрібно переконатися у відповіді сервера правильними заголовками.
Декель

Не могли б ви сказати мені, що потрібен заголовок відповіді на сервері?
Мокс Шах

@MoxShah - це зовсім інше питання, і тут є багато ресурсів :)
Dekel,

9

Ну, я боровся з цим питанням пару тижнів.

Найпростіший, найвідповідальніший і не хакітний спосіб зробити це - це, ймовірно, використовувати провайдер JavaScript API, який не здійснює дзвінки на основі браузера і може обробляти запити Cross Origin.

Наприклад, API JavaScript JavaScript JavaScript і Google JS API.

Якщо ваш постачальник API не є поточним і не підтримує у своїй відповіді заголовок '*' Cross-Origin Resource Origin та не має JS-api (Так, я кажу про вас Yahoo), вам вдарить один із трьох варіантів -

  1. Використовуючи jsonp у своїх запитах, що додає функцію зворотного дзвінка до вашої URL-адреси, де ви можете обробити свою відповідь. Це дозволить змінити URL-адресу запиту, тому ваш сервер API повинен бути обладнаний для обробки? Callback = в кінці URL-адреси.

  2. Надішліть запит на свій сервер API, який є вашим контролером і знаходиться в тому ж домені, що і клієнт, або увімкнено перехресне походження ресурсів, звідки ви можете проксі просити запит на сторонній сервер API.

  3. Напевно, найкорисніше у випадках, коли ви робите запити OAuth та вам потрібно обробляти взаємодію з користувачем Haha! 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 до запиту вручну. Браузер завжди додаватиме опорні заголовки CORS до запиту.
Рей Ніколус

1
Справжньою проблемою є отримання сервером відповіді правильним Access-Control-Allow-Headersта правильним наданням JQ Access-Control-Request-Headers(плюс будь-який доданий вами код), жоден з яких не може бути символами. Для підриву перед польоту потрібен лише один «поганий» заголовок, наприклад, використання If-None-Matchдля умовного GET, якщо сервер не має зазначеного списку.
escape-llc

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 видав мені проблеми з корсом на поштових запитах, де запити на роботу добре працювали - я все набридло вище, без результатів. У мене були правильні заголовки на моєму сервері і т. Д. Перехід на використання 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

Це короткий підсумок того, що працювало для мене:

Визначте нову функцію (обгорнуту $.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 ...


-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 не повинен встановлювати його.
Локшина

Хммф. Це спонукало мої сподівання, а потім їх збило, коли я побачив, що ви робите AJAX "GET" у своєму коді, коли ОП цілком чітко сказав, що він намагається уникнути "GET" і хотів використовувати "POST".
Стів Содер

Заголовки CORS працюють однаково незалежно від дієслова. Ми можемо викликати всі дієслова через $.ajax()правильно налаштований сервер. Найважче - це отримати Access-Control-Request-Headersправильне, але навіть це не надто складно. Як зазначалося в попередніх плакатах, це має бути не підкресленням, а списком заголовків.
escape-llc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.