Книга: JavaScript. Подробное руководство, 6-е издание

18.1.6. Выполнение междоменных НТТР-запросов

18.1.6. Выполнение междоменных НТТР-запросов

Будучи субъектом политики общего происхождения (раздел 13.6.2), объект XMLHttpRequest может использоваться для отправки HTTP-запросов только серверу, откуда был получен использующий его документ. Это ограничение закрывает дыру безопасности, но также предотвращает возможность использования объекта для вполне законных междоменных запросов. Элементы <form> и <iframe> позволяют указывать URL-адреса с другими доменными именами, и в этих случаях броузеры будут отображать документы, полученные из других доменов. Но из-за ограничений политики общего происхождения броузер не позволит оригинальному сценарию исследовать содержимое стороннего документа. При использовании объекта XMLHttpRequest содержимое документа всегда доступно через свойство responseText, поэтому политика общего происхождения не позволяет объекту XMLHttpRequest выполнять междоменные запросы. (Обратите внимание, что элемент <script> в действительности никогда не был субъектом политики общего происхождения: он будет загружать и выполнять любые сценарии, независимо от их происхождения. Как будет показано в разделе 18.2, такая свобода выполнения междоменных запросов делает элемент <script> привлекательной альтернативой Ajax-транспорту на основе объекта XMLHttpRequest.)

Спецификация «ХНН2» позволяет выполнять междоменные запросы к другим веб-сайтам, указанным в заголовке «CORS» (Cross-Origin Resource Sharing) HTTP-ответа. На момент написания этих строк текущие версии броузеров Firefox, Safari и Chrome поддерживали заголовок «CORS», а версия IE8 поддерживала собственный объект XDomainRequest, который здесь не описывается. Веб-программисту при этом не придется делать ничего особенного: если броузер поддерживает заголовок «CORS» для объекта XMLHttpRequest и если вы пытаетесь выполнить междоменный запрос к веб-сайту, доступ к которому разрешен в заголовке «CORS», политика общего происхождения просто не будет препятствовать выполнению междоменного запроса, и все будет работать само собой.

Хотя при наличии поддержки заголовка «CORS» не требуется предпринимать какие-то дополнительные шаги для выполнения междоменных запросов, тем не менее имеются некоторые детали, касающиеся безопасности, которые желательно понимать. Во-первых, при передаче имени пользователя и пароля методу open() объекта XMLHttpRequest они не будут передаваться в междоменных запросах (в противном случае это дало бы злоумышленникам возможность взламывать пароли). Кроме того, междоменные запросы обычно не допускают включение в них никакой другой информации, подтверждающей подлинность пользователя: cookies и HTTP-лексемы аутентификации обычно не передаются в составе запроса, а любые cookies, полученные в результате выполнения междоменного запроса, уничтожаются. Если для выполнения междоменного запроса требуется выполнить аутентификацию пользователя, перед вызовом метода send() следует установить свойство withCredentials объекта XMLHttpRequest в значение true. Проверка наличия свойства withCredentials позволит определить наличие поддержки заголовка «CORS» в броузере, хотя обычно этого не требуется.

В примере 18.13 приводится ненавязчивый JavaScript-код, использующий объект XMLHttpRequest для выполнения HEAD-запросов HTTP с целью получения информации о типах, размерах и датах последнего изменения ресурсов, на которые ссылаются элементы <а>. HEAD-запросы выполняются по требованию, а полученная информация отображается во всплывающих подсказках. В этом примере предполагается, что информация не будет доступна для междоменных ссылок, тем не менее в броузерах с поддержкой заголовка «CORS» все равно будут выполняться попытки получить ее.

Пример 18.13. Получение информации о ссылках с помощью HEAD-запросов при наличии поддержки заголовка «CORS»

/**
* linkdetails.js
*
* Этот модуль в стиле ненавязчивого JavaScript отыскивает все элементы <а>
* с атрибутом href и без атрибута title, и добавляет в них обработчики
* события onmouseover. Обработчик события выполняет HEAD-запрос с помощью
* объекта XMLHttpRequest, чтобы получить сведения о ресурсе, на который
* указывает ссылка, и сохраняет эту информацию в атрибуте title ссылки,
* благодаря чему эта информация будет отображаться во всплывающей подсказке.
*/
whenReady(function() {
  // Поддерживается ли возможность выполнения междоменных запросов?
  var supportsCORS = (new XMLHttpRequest()).withCredentials !== undefined;
  // Обойти в цикле все ссылки в документе
  var links = document.getElementsByTagName('a');
  for(var і = 0; і < links.length; і++) {
    var link = links[і];
    if (!link.href) continue; // Пропустить якоря, не являющиеся ссылками
    if (link.title) continue; // Пропустить ссылки с атрибутом title
    // Если это междоменная ссылка
    if (link.host!==location.host || link.protocol !== location.protocol)
    {
      link.title = "Ссылка на другой сайт"; // Предполагается, что нельзя
                                            // получить дополнительную информацию
      if (!supportsCORS) continue; // Пропустить, если заголовок
                                   // C0RS не поддерживается
      // Иначе есть надежда получить больше сведений о ссылке. Поэтому регистрируем
      // обработчик события, который предпримет попытку сделать это.
    }
    // Зарегистрировать обработчик события, который получит сведения
    // о ссылке при наведении на нее указателя мыши
    if (link.addEventListener)
      link.addEventListener("mouseover", mouseoverHandler, false);
    else
      link.attachEvent("onmouseover", mouseoverHandler);
  }
  function mouseoverHandler(e) {
    var link = e.target || e.srcElement; // Элемент <a>
    var url = link, liref; // URL-адрес ссылки
    var req = new XMLHttpRequest(); // Новый запрос
    req.open("HEAD", url); // Запросить только заголовки
    req.onreadystatechange = function() { // Обработчик события
      if (req.readyState !== 4) return; // Игнорировать незаверш. запросы
      if (req.status === 200) { // В случае успеха
        var type = req.getResponseHeader("Content-Type"); //Получить
        var size = req.getResponseHeader("Content-Length"); //сведения
        var date = req.getResponseHeader("Last-Modified"); //о ссылке
        // Отобразить сведения во всплывающей подсказке,
        link.title = "Тип: " + type + " n" +
                     "Размер: " + size + " n" + "Дата: " + date;
      }
      else {
        // Если запрос не удался и подсказка для ссылки еще не содержит текст
        // "Ссылка на другой сайт", вывести сообщение об ошибке,
        if (!link.title)
          link.title = "Невозможно получить сведения: n" + req.status + " " + req.statusText;
      }
    };
    req.send(null);
    // Удалить обработчик: попытка получить сведения выполняется только один раз.
    if (link.removeEventListener)
      link.removeEventListener("mouseover", mouseoverHandler, false);
    else
      link.detachEvent("onmouseover", mouseoverHandler);
  }
});

Оглавление книги


Генерация: 1.174. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз