Эта популярная статья была обновлена 23 июня 2016 года для решения вопросов качества. Комментарии, относящиеся к старой статье, были удалены.
Если вы разрабатываете веб-приложение и пытаетесь загрузить данные из домена, который не находится под вашим контролем, есть вероятность, что вы увидели следующее сообщение в консоли вашего браузера:
XMLHttpRequest не может загрузить http: // external-domain / service. В запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Исходный код «http: // my-domain», следовательно, не имеет доступа.
В этой статье мы рассмотрим, что вызывает эту ошибку и как ее обойти, используя jQuery и JSONP для выполнения междоменного вызова Ajax.
Политика одного происхождения
Обычные веб-страницы могут использовать объект XMLHttpRequest для отправки и получения данных с удаленных серверов, однако они ограничены в том, что они могут делать с помощью одной и той же политики источника . Это важная концепция в модели безопасности браузера и требует, чтобы веб-браузер разрешал сценариям на странице A только доступ к данным на странице B, если эти две страницы имеют одинаковое происхождение. Происхождение страницы определяется ее протоколом , хостом и номером порта . Например, источником этой страницы является «https», «www.sitepoint.com», «80».
Политика того же происхождения — это безопасный механизм. Он не позволяет сценариям считывать данные с вашего домена и отправлять их на свои серверы. Если бы у нас этого не было, злоумышленнику было бы легко захватить информацию о вашей сессии на другом сайте (например, Gmail или Twitter) и выполнить действия от вашего имени. К сожалению, это также вызывает ошибку, которую мы видим выше, и часто создает головную боль для разработчиков, пытающихся выполнить законную задачу.
Плохой пример
Давайте посмотрим на то, что не работает. Вот файл JSON, расположенный в другом домене, который мы хотели бы загрузить, используя метод getJSON в jQuery.
$.getJSON( "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json", function(json) { console.log(json); } );
Если вы попробуете это в своем браузере с открытой консолью, вы увидите сообщение, подобное приведенному выше. Так что мы можем сделать?
Возможное решение
К счастью, не все зависит от политики одного происхождения. Например, вполне возможно загрузить изображение или скрипт из другого домена на вашу страницу — это именно то, что вы делаете, когда вы включаете (например) jQuery из CDN.
Это означает, что мы можем создать <script>
, установить для атрибута src
атрибут нашего файла JSON и вставить его на страницу.
var script = $("<script />", { src: "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json", type: "application/json" } ); $("head").append(script);
Хотя это работает, это не очень нам помогает, так как у нас нет возможности получить доступ к содержащимся в нем данным.
Введите JSONP
JSONP (что означает JSON with Padding) основывается на этом методе и предоставляет нам способ доступа к возвращенным данным. Это достигается тем, что сервер возвращает данные JSON, заключенные в вызов функции («заполнение»), который затем может интерпретироваться браузером. Эта функция должна быть определена на странице, оценивающей ответ JSONP.
Давайте посмотрим, как это будет выглядеть в нашем предыдущем примере. Вот обновленный файл JSON, который оборачивает исходные данные JSON в функцию jsonCallback
.
function jsonCallback(json){ console.log(json); } $.ajax({ url: "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-2.json", dataType: "jsonp" });
Это записывает ожидаемый результат на консоль. Теперь у нас есть (хотя и довольно ограниченный) междоменный Ajax.
Сторонние API
Некоторые сторонние API позволяют указывать имя для функции обратного вызова, которая должна выполняться при возврате запроса. Одним из таких API является GitHub API .
В следующем примере мы получаем информацию о пользователе для Джона Ресига (создателя jQuery) и используем logResults
обратного вызова logResults
для записи ответа на консоль.
function logResults(json){ console.log(json); } $.ajax({ url: "https://api.github.com/users/jeresig", dataType: "jsonp", jsonpCallback: "logResults" });
Это также можно записать как:
$.getJSON("https://api.github.com/users/jeresig?callback=?",function(json){ console.log(json); });
?
в конце URL сообщает jQuery, что он имеет дело с запросом JSONP вместо JSON. Затем jQuery автоматически регистрирует функцию обратного вызова, которую он вызывает при повторном выполнении запроса.
Если вы хотите узнать больше о методе getJSON
jQuery, ознакомьтесь с: Ajax / jQuery.getJSON Простой пример
Предостережения
Но, как вы уже поняли, у этого подхода есть некоторые недостатки.
Например, JSONP может выполнять только междоменные запросы GET, и сервер должен явно поддерживать его. JSONP также не без проблем с безопасностью , поэтому давайте кратко рассмотрим некоторые другие решения.
Использование прокси
Серверный код не связан одной и той же политикой происхождения и может без проблем выполнять запросы между источниками. Поэтому вы можете создать какой-нибудь прокси и использовать его для получения любых данных, которые вам нужны. Со ссылкой на наш первый пример:
/* proxy.php */ $url = "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec ($ch); curl_close ($ch); echo $result;
И на стороне клиента:
$.getJSON("http://my-domain.com/proxy.php", function(json) { console.log(json); })
Но этот подход также имеет свои недостатки. Например, если сторонний сайт использует куки для аутентификации, это не будет работать.
CORS
Распределение ресурсов между источниками (CORS) — это спецификация W3C, позволяющая междоменную связь из браузера. Это делается путем включения в ответ нового HTTP-заголовка Access-Control-Allow-Origin
.
Со ссылкой на наш первый пример вы можете добавить следующее в файл .htaccess
(предполагается, что Apache), чтобы разрешить запросы из разных источников:
Header add Access-Control-Allow-Origin "http://my-domain.com"
( Если у вас есть сервер, на котором работает что-то отличное от Apache, посмотрите здесь: http://enable-cors.org/server.html )
Вы можете узнать больше о CORS в одном из наших последних учебных пособий: Углубленный взгляд на CORS
Вывод
JSONP позволяет обойти политику одного источника и в некоторой степени выполнять междоменные вызовы Ajax. Это не серебряная пуля, и у нее, безусловно, есть свои проблемы, но в некоторых случаях она может оказаться неоценимой при получении данных из другого источника.
JSONP также позволяет получать разнообразный контент из разных сервисов. Многие известные сайты предоставляют сервисы JSONP (например, Flickr ), позволяющие вам получить доступ к их контенту через предопределенный API. Вы можете найти их полный список в каталоге API ProgrammableWeb .