Создать CORS запрос

В этом разделе показано, как сделать меж-доменный запрос JavaScript.

Создание объекта XMLHttpRequest

CORS поддерживается следующими браузерами:

  • Chrome 3 +
  • Firefox 3.5 +
  • Safari 4 +
  • Internet Explorer 8 +

Chrome, Firefox и Safari все используют объекты XmlHttpRequest2. Internet Explorer использует подобный объект XDomainRequest, который работает во многом так же, как и ее коллега XmlHttpRequest, но добавляет дополнительные меры безопасности.

Чтобы начать, вам сначала нужно создать соответствующий запрос. Nicholas Zakas написал простой вспомогательный метод, чтобы помочь разобрать различия в браузере:



function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {

    // Check if the XMLHttpRequest object has a "withCredentials" property.
    // "withCredentials" only exists on XMLHTTPRequest2 objects.
    xhr.open(method, url, true);

  } else if (typeof XDomainRequest != "undefined") {

    // Otherwise, check if XDomainRequest.
    // XDomainRequest only exists in IE, and is IE's way of making CORS requests.
    xhr = new XDomainRequest();
    xhr.open(method, url);

  } else {

    // Otherwise, CORS is not supported by the browser.
    xhr = null;

  }
  return xhr;
}

var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}


Обработчики событий

Был только один обработчик событий; onreadystatechange, который обрабатывал все ответы. Хотя onreadystatechange по прежнему доступен, XmlHttpRequest2 вводит несколько новых обработчиков событий. Вот полный список:

Обработчик событий Описание
onloadstart * Если запрос начался.
OnProgress Во время загрузки и отправки данных.
onabort * Когда запрос был прерван. Например, при вызове метода interrupt ().
OnError Если запрос не удался.
OnLoad Если запрос успешно завершен.
ontimeout Когда автор указан в конце, прежде чем запрос был завершен.
onloadend * Завершение запроса (успех или неудача).

* Элементы не поддерживается в IE XDomainRequest.

В большинстве случаев, вы хотите обрабатывать OnLoad и OnError события:



xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // process the response.
};

xhr.onerror = function() {
  console.log('There was an error!');
};


Браузеры не делают хорошую отчетность, когда что то пошло не так, или есть ошибки. Например у Firefox отчетность статус 0 а пустая StatusText для всех ошибок. Браузеры также сообщают об ошибке в журнале консоли, но это сообщение не может быть доступно из JavaScript. При работе с OnError, вы будете знать, что произошла ошибка, но не более того.

withCredentials

Стандартные запросы CORS не отправляют и не устанавливают куки по умолчанию. Для того чтобы включить куки как часть запроса, вам нужно установить XmlHttpRequest в withCredentials значение true.



xhr.withCredentials = true;


Для того чтобы это работало, сервер должен позволить установить Access-Control-Allow-Credentials. Смотрите  раздел .



Access-Control-Allow-Credentials: true


WithCredentials будет включать в себя любые куки с удаленного домена в запросе, и будет также устанавливать куки из удаленного домена. Обратите внимание, что ваш JavaScript код не может получить доступ к куки с document.cookie или заголовков ответа. Они контролируется только с удаленного домена.

Создание запроса

Теперь, когда ваш CORS запроса настроен, вы готовы сделать запрос. Это делается путем вызова метода send() :



xhr.send();


Если запрос имеет тело, оно может быть указано в качестве аргумента для  send() .

И Все! Сервер настроен правильно для запросов CORS, ваш OnLoad обработчик срабатывает с ответом, одного и того же домена XHR.

Вот полный рабочий образец запроса CORS.



// Create the XHR object.
function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {
    // XHR for Chrome/Safari/Firefox.
    xhr.open(method, url, true);
  } else if (typeof XDomainRequest != "undefined") {
    // XDomainRequest for IE.
    xhr = new XDomainRequest();
    xhr.open(method, url);
  } else {
    // CORS not supported.
    xhr = null;
  }
  return xhr;
}

// Helper method to parse the title tag from the response.
function getTitle(text) {
  return text.match('<title>(.*)?</title>')[1];
}

// Make the actual CORS request.
function makeCorsRequest() {
  // bibliographica.org supports CORS.
  var url = 'http://bibliographica.org/';

  var xhr = createCORSRequest('GET', url);
  if (!xhr) {
    alert('CORS not supported');
    return;
  }

  // Response handlers.
  xhr.onload = function() {
    var text = xhr.responseText;
    var title = getTitle(text);
    alert('Response from CORS request to ' + url + ': ' + title);
  };

  xhr.onerror = function() {
    alert('Woops, there was an error making the request.');
  };

  xhr.send();
}


Добавление CORS, поддержка сервера

Браузеру и серверу CORS обрабатывать тяжело. Браузер добавляет некоторые дополнительные заголовки, а иногда делает дополнительные запросы, во время запроса CORS от имени клиента. Эти дополнения скрыты от клиента (но могут быть обнаружены с Wireshark.)


CORS поток

В этом разделе объясняется, как настроить сервер и его заголовки для поддержки CORS.

Типы запросов CORS

Кросс запросы бывают двух видов:

  1. Простые запросы
  2. «Не простые запросы» ( я придумал)

Простые запросы, отвечают следующим критериям:

Методы HTTP (с учетом регистра) одним из следующих:

  • HEAD
  • GET
  • POST

Заголовки HTTP (без учета регистра):

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type, но только если значение является одним из:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Браузеру не нужен CORS для того чтобы сделать простой запрос, простые запросы можно сделать с помощью других методов браузера. Например, JSON-P запрос может выдать меж-доменные GET запросы. HTML может использоваться, чтобы сделать форму POST.

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

Простой запрос CORS

Начнем с простого запроса от клиента. Приведенная ниже таблица показывает код JavaScript для простого запроса GET, а также фактический запрос HTTP, который выдаёт браузер; CORS заголовки жирным шрифтом.

JavaScript:



var url = 'http://alice.com/cors';
var xhr = createCORSRequest('GET', url);
xhr.send();


Запрос HTTP:



GET /cors HTTP/1.1
Origin: http://api.alice.com
Host: api.bob.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Прежде всего следует отметить, что правильный запрос CORS * всегда * содержит происхождение заголовка. Это происхождение заголовка добавляется в браузере, и не может управляться пользователем. Значение этого заголовка схемы (например, HTTP), домен (например bob.com) и порт (в комплекте только если это не порт по умолчанию, например, 81), из которого запрос исходит, например: http://api . alice.com.

Вот правильный ответ сервера; CORS конкретные заголовки выделены жирным шрифтом

Ответ HTTP:



Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8


Все связанные заголовки CORS начинаются с «Access-Control». Вот некоторые подробности о каждом заголовке.

Access-Control-Allow-Origin (обязательно) — этот заголовок должен быть включен во все ответы CORS; опуская заголовок вызова CORS запрос на провал. Значение заголовка может либо эхо-запрос происхождение заголовка (как в примере выше), или ‘*’, запросы от любого происхождения. Если вы хотите сделать какой-либо сайт, чтобы иметь возможность получить доступ к данным, используя ‘*’. Но если вы хотите более точного управления, которое может получить доступ к данным, используйте фактические значения в заголовке.

Access-Control-Allow-Credentials (опционально) — по умолчанию, cookies, не включаются в CORS запросы. Используйте этот заголовок, чтобы указать, что куки должны быть включены в CORS запросы. Единственным допустимым значением для этого заголовка true (все строчными буквами). Если вам не нужны куки, не включать этот заголовок (а не устанавливать значение false).

Access-Control-Allow-Credentials заголовок работает в связке с объектами withCredentials на XmlHttpRequest2. Оба эти свойства должны быть установлены в true для того, чтобы запрос CORS был успешный. Если WithCredentials является true, Access-Control-Allow-Credentials то запрос не сработает (и наоборот).

Рекомендуется не устанавливать этот заголовок, если вы не уверены, что куки включены в запросы CORS.

Access-Control-Expose-Headers (опционально) — объект XmlHttpRequest2 имеет метод getResponseHeader() , который возвращает значение определенного заголовка. Во время запроса CORS, getResponseHeader() может получить доступ только к простым заголовкам ответа. Простые заголовки ответа определяются следующим образом:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Если вы хотите что бы пользователи могли получать доступ к другим заголовкам, вы должны использовать заголовок Access-Control-Expose-Headers. Значение этого заголовка разделенные запятыми список заголовков ответа если вы хотите их сделать видимыми для пользователя.

На момент написания статьи, все браузеры имеют багги реализаций  getRequestHeader() , так что заголовки не могут быть доступны для пользователей даже после установки заголовки Access-Control-Expose-Headers. Смотрите  известные ошибки  раздел ниже для более подробной информации.

Не простой запрос CORS

Не простой запрос выглядит как один запрос к клиенту, но на самом деле состоит из двух запросов в целом. Браузер первых вопросов предполетной запрос, который просит сервер для разрешения сделать фактический запрос. Как только разрешение было предоставлено, браузер делает фактический запрос. Браузер обрабатывает детали этих двух запросов в фоне. Предполетной ответ также может быть кэшированы так, чтобы не выдается при каждом запросе.

Пример не простого запроса:

JavaScript:



var url = 'http://alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader
('X-Custom-Header', 'value');
xhr.send();


Предполетный запрос



OPTIONS /cors HTTP/1.1
Origin: http://api.alice.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.bob.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Как и простой запрос, браузер добавляет Происхождение заголовка к каждой просьбе, в том числе предполетных. Предполетный запрос сделан как запрос HTTP OPTIONS. Он также содержит несколько дополнительных заголовков:

Access-Control-Request-Method — HTTP метод фактический запрос. Этот запрос заголовок всегда включен, даже если метод HTTP является простой метод HTTP, как определено ранее (GET, POST, HEAD).

Access-Control-Allow-Headers — разделенные запятыми список непростых заголовков, которые включены в запрос.

Предполетный запрос способ просить разрешения на фактический запрос, прежде чем сделать фактический запрос. Сервер должен проверить два заголовка выше, чтобы убедиться, что оба метода HTTP и просит заголовки являются ли действительными и принимаются.

Если метод HTTP и заголовки правильные, сервер должен отвечать следующее:

Предполетный Запрос:



OPTIONS /cors HTTP/1.1
Origin: http://api.alice.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.bob.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Предполетный характеристика:



Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8


Access-Control-Allow-Origin (обязательно) — Как простой ответ, предполетный ответ должен включать этот заголовок.

Access-Control-Allow-Methods (обязательно) — разделенных запятыми список поддерживаемых методов HTTP. Отметим, что хотя предполетной запрос только просит permisions для одного метода HTTP, этот заголовок ответа может включать в себя перечень всех поддерживаемых методов HTTP. Это полезно, потому предполетный ответ может находиться в кэше, так что одно предполетной ответ может содержать информацию о нескольких типах запроса.

Access-Control-Allow-Headers (требуется, если запрос Access-Control-Request-Headers заголовка) — разделенных запятыми список поддерживаемых заголовков запроса. Как Access-Control-Allow-Methods, это может создать список всех заголовков, поддерживаемых сервером (а не только заголовки просил в предполетном запросе).

Access-Control-Allow-Credentials (опционально) — То же, что простой запрос.

Access-Control-Max-Age (опционально) — Создание предполетного запроса на * каждый * Запрос становится дорогим, так как браузер делает два запроса для каждого клиентского запроса. Значение этого заголовка позволяет предполетной ответ на кэширование в течение указанного количества секунд.

После предполетный запрос дает разрешение, браузер делает фактический запрос. Фактический запрос выглядит как простой запрос, и ответ должен быть обработан таким же образом:

Фактические Запрос:



PUT /cors HTTP/1.1
Origin: http://api.alice.com
Host: api.bob.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Фактический диапазон:



<b>Access-Control-Allow-Origin: http://api.bob.com</b><br>Content-Type: text/html; charset=utf-8


Если метод HTTP-заголовки или просил в предполетный не являются действительными, сервер может просто вернуть общий ответ, как HTTP 200. Есть не CORS конкретные заголовки в ответ, браузер принимает запрос и он является недействительным, и не делает фактический запрос:

Предполетный Запрос:



OPTIONS /cors HTTP/1.1
Origin: http://api.alice.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.bob.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Характеристика предполетного запроса:



// ERROR - No CORS headers, this is an invalid request!
Content-Type: text/html; charset=utf-8


Если есть ошибки в запросе CORS, браузер будет снимать клиента OnError обработчиком событий. Он также выведет следующее сообщение об ошибке в журнале консоли:

XMLHttpRequest не может загрузить http://api.alice.com. Происхождение http://api.bob.com не допускается Access-Control-Allow-Origin.

Браузер не даст вам много подробностей о том, почему произошла ошибка, он только говорит вам, что что-то пошло не так.

Несколько слов о безопасности

Хотя CORS закладывает основу для создания кросс-доменных запросов, CORS заголовки не заменит применения методов защиты. Вы не должны полагаться на CORS заголовок для обеспечения ресурсов на вашем сайте. Используйте CORS заголовки, чтобы дать браузеру направлений на междоменный доступ, но и использовать другие механизмы безопасности, такие как cookies или OAuth2, если вам нужны дополнительные ограничения безопасности на ваше содержание.

Междоменные из расширений Chrome

Chrome расширения поддержки кросс-доменных запросов с двумя различными способами:

  1. Включать домен в manifest.json — Chrome расширения могут сделать кросс-доменных запросов в любой домен * * если домен входит в «разрешения» раздела manifest.json файл:
    
    
    "permissions": [ "http://*.html5rocks.com"]
    
    
    

    Эти запросы не включают Происхождение заголовка, и смотрят, как будто они идут из самого браузера.

  2. CORS запросу — Если домен не находится в файле manifest.json, то расширение Chrome делает стандартный запрос CORS. Значение Происхождения заголовок «chrome-extension://[CHROME EXTENSION ID]». Это означает, что запросы от расширений Chrome подлежат тем же правилам CORS, описанные в этой статье.

Известные ошибки

CORS поддержка по-прежнему активно развивается во всех браузерах, вот список известных ошибок (по состоянию на 14/11/2011):

  1. GetAllResponseHeaders XMLHttpRequests ‘() не выполняет Access-Control-Expose-заголовки — getAlLResponseHeaders () для объекта XMLHttpRequest должен вернуть все простые заголовки ответа и любой заголовок ответа, включенные в Access-Control-Expose-Headers заголовок ответа. В Chrome / Safari, только простые заголовки ответа включены, в то время как Firefox не возвращает никаких заголовков ответа (ошибка WebKit).
  2. Непростые заголовки GET / POST запросы обойти предполетным запросом — В Safari запрос с простым методом HTTP (GET или POST), но не простые заголовки запроса, не выдаёт предполетной просьбу, непростые заголовоки не включены в качестве части запроса (ошибка WebKit ).
  3. Нет ошибок информации, предоставленной OnError Обработчик — Когда OnError Обработчик уволен, код состояния равен 0, и нет StatusText. Это может быть по определению, но это может и привести к путанице при попытке отладки, почему CORS запросы не удаеются.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>