Помилка jQuery xml 'На запитаному ресурсі немає заголовка' Access-Control-Allow-Origin '.'


89

Я працюю над цим своїм особистим проектом просто для розваги, де хочу прочитати файл xml, який знаходиться за адресою http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml та проаналізувати xml та використовуйте його для перетворення значень між валютами.

Поки що я придумав наведений нижче код, який є досить базовим для читання xml, але я отримую таку помилку.

XMLHttpRequest не може завантажити ****. На запитаному ресурсі немає заголовка 'Access-Control-Allow-Origin'. Отже, джерело " http://run.jsbin.com " не має доступу.

$(document).ready( 
    function() {     
        $.ajax({          
            type:  'GET',
            url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
            dataType: 'xml',              
            success: function(xml){
                alert('aaa');
            }
         });
    }
);

Я не бачу нічого поганого в своєму коді, тому сподіваюся, хтось може вказати, що я роблю неправильно зі своїм кодом, і як я можу це виправити.


2
Пропоную вам ознайомитись з Політикою щодо самого походження та CORS
jmoerdyk

помилка вказує, що саме не так, слово в слово. Ваш код чудовий, проблема в сервері, до якого ви отримуєте доступ.
Kevin B

а також див. CORS на MDN
Amir Ali Akbari

Відповіді:


163

Ви не зможете здійснити виклик ajax http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlіз файлу, розгорнутого в, http://run.jsbin.comчерез політику того самого походження .


В якості джерела (він же походження ) сторінки і цільової URL знаходяться на різних доменах ( run.jsbin.comі www.ecb.europa.eu), ваш код насправді намагається зробити крос-домен (CORS) запит, а не звичайний GET.

У кількох словах політика того самого походження говорить, що браузери повинні дозволяти лише дзвінки ajax до служб у тому ж домені сторінки HTML.


Приклад:

Сторінка за адресою http://www.example.com/myPage.htmlможе безпосередньо запитувати послуги, які знаходяться на http://www.example.com, наприклад http://www.example.com/api/myService. Якщо послуга розміщується в іншому домені (скажімо http://www.ok.com/api/myService), браузер не буде здійснювати дзвінок безпосередньо (як ви очікували). Натомість він спробує зробити запит CORS.

Якщо коротко, то для виконання запиту (CORS) * у різних доменах, ваш браузер:

  • Буде включати Originзаголовок у вихідний запит (із значенням домену сторінки) та виконувати його як зазвичай; і потім
  • Тільки якщо сервер відповідь на цей запит містить адекватні заголовки ( Access-Control-Allow-Originце один з них ) дозволяючи CORS запит, переглядає завершить виклик (майже ** саме так , як це було б , якщо сторінка HTML була в тому ж домені).
    • Якщо очікувані заголовки не з’являються, браузер просто здається (як це зробив вам).


* Вище наведено кроки у простому запиті, наприклад, звичайний GETбез вигадливих заголовків. Якщо запит не є простим (наприклад, a POSTз application/jsonтипом вмісту), браузер затримає його хвилинку і, перш ніж виконати його, спочатку надішле OPTIONSзапит на цільову URL-адресу. Як і вище, він буде продовжуватися, лише якщо відповідь на цей OPTIONSзапит містить заголовки CORS. Цей OPTIONSдзвінок відомий як попередній запит.
** Я кажу майже тому, що існують інші відмінності між звичайними дзвінками та дзвінками CORS. Важливим є те, що деякі заголовки, навіть якщо вони присутні у відповіді, не будуть підхоплені браузером, якщо вони не включені вAccess-Control-Expose-Headers заголовок.


Як це виправити?

Це була просто друкарська помилка? Іноді код JavaScript має лише помилку в цільовому домені. Ви перевірили? Якщо сторінка знаходиться на www.example.comній, будуть здійснюватися лише регулярні дзвінки www.example.com! Інші URL-адреси, такі як api.example.comабо навіть example.comабо www.example.com:8080вважаються браузером різними доменами! Так, якщо порт інший, то це інший домен!

Додайте заголовки. Найпростіший спосіб увімкнути CORS - це додавання необхідних заголовків (as Access-Control-Allow-Origin) до відповідей сервера. (Кожен сервер / мова має спосіб зробити це - перевірте деякі рішення тут .)

Крайній засіб: якщо у вас немає доступу до служби на стороні сервера, ви також можете його віддзеркалити (за допомогою таких інструментів, як зворотні проксі-сервери ) і включити туди всі необхідні заголовки.


2
Дякую, це багато інформації. Тепер я можу провести необхідне дослідження, щоб продовжити.
Bazinga777

1
Привіт acdcjunior, як відобразити веб-службу, до якої я хочу отримати доступ?
Франва

2
@Franva Вам доведеться встановити HTTP-сервер (наприклад, Tomcat, Apache з PHP, IIS з ASP) і розмістити там сторінку, яка при кожному запиті відкриває сокет для фактичної служби (служби, яку ви дзеркально відображаєте), запитує фактичні дані, а потім надає їх як відповідь. Звичайно, ви зробите це за допомогою коду (Java, PHP, ASP тощо).
acdcjunior

@acdcjunior Будь ласка, виправте мене, якщо я розумію правильно. Якщо я вводжу якусь URL-адресу безпосередньо у браузері, вона автоматично перенаправить на нову URL-адресу домену без Access-Control-Allow-Origin . Наприклад, при використанні WIF користувач буде перенаправлений на сторінку входу сторонніх розробників під час першого входу.
машинарій

@machinarium Я не впевнений, що я розумію, що ти мав на увазі, але я спробую відповісти (скажи, чи я щось помилився): якщо ти введеш URL-адресу в адресному рядку браузера, наявність або відсутність Access-Control-Allow-Originу цій URL-адресі заголовки не матимуть значення - браузер відкриє URL-адресу, як зазвичай. Політика того самого походження (і вимога до Access-Control-Allow-Originзаголовка) застосовується лише до дзвінків Ajax.
acdcjunior

29

Існує своєрідний хакерський спосіб зробити це, якщо на вашому сервері увімкнено php. Змінити цей рядок:

url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',

до цього рядка:

url: '/path/to/phpscript.php',

а потім у php-скрипті (якщо у вас є дозвіл на використання функції file_get_contents ()):

<?php

header('Content-type: application/xml');
echo file_get_contents("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

?>

Здається, Php не заперечує, якщо ця URL-адреса іншого походження. Як я вже сказав, це хитка відповідь, і я впевнений, що з нею щось не так, але вона працює для мене.

Редагувати: якщо ви хочете кешувати результат у php, ось файл php, який ви б використовували:

<?php

$cacheName = 'somefile.xml.cache';
// generate the cache version if it doesn't exist or it's too old!
$ageInSeconds = 3600; // one hour
if(!file_exists($cacheName) || filemtime($cacheName) > time() + $ageInSeconds) {
  $contents = file_get_contents('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml');
  file_put_contents($cacheName, $contents);
}

$xml = simplexml_load_file($cacheName);

header('Content-type: application/xml');
echo $xml;

?>

Код кешування взяти звідси .


3
Ще кращим рішенням було б кешувати XML-файл на стороні сервера і виконувати file_get_contentsвиклик, лише якщо останній XML-файл має достатню дату. Крім того, не забудьте свій заголовок Content-Type :-)
sffc

Натрапив на цю відповідь. Питання: Звідки файл PHP знає, як взяти дані GET і надіслати їх за вказаною URL-адресою? Чи буде це працювати з даними POSTed?
mpdc

Він цього не подає. Він отримує файл і зберігає його локально, а потім видає його так, ніби цей файл - це php-файл, який ви запитуєте локально. Він отримає та збереже іншу його копію, якщо локально кешований файл перевищує вказаний максимальний вік.
Думка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.