Pull to refresh

Userscripts. Кроссдоменные запросы

Reading time 5 min
Views 50K
Доброго времени суток.
Сегодня мы рассмотрим варианты организации кроссдоменных запросов в юзерскриптах.
В подробности реализации того или иного механизма я вдаваться не буду, но приведу пример кроссбраузерной оболочки для кроссдоменных запросов.

Вступление



Как известно, в чистом виде в естественной среде браузерного окружения кроссдоменные запросы — штука не тривиальная (благодаря Same origin policy).
Поддержка XMLHttpRequest с CORS есть не во всех браузерах (в опере вообще до сих пор не встречена).
JSONP позволяет только GET-запросы, а проксирование через фреймы заставляет использовать сторонние библиотеки, либо писать жуткие велосипеды.

Перерыв:Если вы дочитали до этого места, и не поняли ни слова, то сделайте перерыв, погуглите неизвестные термины, выпейте чаю. Не унывайте, дальше будут объяснения «на пальцах» и с кодом. Начинающие скриптописатели могут вздохнуть спокойно: вдаваться в подробности мы не будем, но рабочий код и капля теории со всеми ссылками будет в вашем «загашнике».

Основы


В чем проблематичность кроссдоменных запросов? Все браузеры ограничивают нашу свободу в угоду безопасности. Посему, запросы из «недоверенных» источников пресекаются на корню. А что делать, если мы источникам доверяем? Правильно, использовать «костыли» (многие из которых являются общепринятой практикой).

В рамках юзерскриптов следует учитывать ограничения, накладываемые окружением, в котором запускаются скрипты (подробности в этой статье) плюс дополнительное важное условие: наличие доступа к серверной части (наличие (или возможность создать) серверного API для реализации конкретного метода запроса)!

Ниже приведена таблица, в которой представлены некоторые «костыли» и их применимость для юзерскриптов.
Название метода + ссылка Краткое описание Требует доступ к серверу Можно использовать в юзерскриптах
JSONP

Статья на Хабре
Только GET-запросы.
Информация запрашивается посредством вставки в тело документа тега <script>. Ответ сервера попадает в тело скрипта и исполняется браузером.
При этом, в скрипте должна быть определена функция-«обёртка», которая передаётся с сервера и содержится в ответе.
Требуется доступ или JSONP API
IE7+
Opera
Firefox
XHR + CORS

Статья на Хабре
Статья на Mozilla Hacks
Запрос осуществляется посредством XMLHttpRequest (XHR) с указанием специальных заголовков запроса.
Требуется доступ или XHR CORS API
IE8+
Firefox
Chrome (extension)
iframe транспорт
Запрос осуществляется через размещение в документе невидимого айфрейма, который возвращает ответ в родительское окно. Довольно запутанный способ.
Требуется доступ. Удобнее всего использовать библиотеку easyXDM (нужна небольшая модификация для Chrome и Firefox).
IE7+
Firefox
Opera
Chrome (extension)
Родные возможности юзерскриптов
Специфичны для каждого браузера. В IE отсутствуют как класс.
Firefox + GreaseMonkey предоставляют GM_xmlHttpRequest (аналог XMLHttpRequest), GET+POST запросы.
Chrome предоставляет полноценный кроссдоменный XMLHttpRequest, но только для расширений, GET+POST запросы.
У Opera есть событие beforeScript, при помощи которого можно осуществить GET-запрос через <script> (не JSONP, следовательно, доступ к серверу не нужен).
Не требуется доступ к серверу.
Firefox
Opera
Chrome (extension)


Для подавляющего большинства скриптов критичным параметром является наличие доступа к серверу. К тому же, чаще всего используются GET-запросы (если скрипт не зловред, не наделён мегаинтеллектом или не является частью какого-либо сервиса).

Просмотрев таблицу на трезвую голову внимательно, можно составить примерный перечень способов организации кроссдоменных запросов для кроссбраузерного юзерскрипта:
  • Chrome = упаковка + проксирование XMLHttpRequest (описано в третьей статье)
  • Firefox + GreaseMonkey = использование GM_xmlHttpRequest
  • Opera = использование события beforeScript и скрипт-транспорта
  • IE7+ = использование JSONP

Недостатки:
  • Только GET-запросы (не критично для 90%)
  • Для IE нужен доступ к серверу или JSONP API (не критично для 90%, для остальных можно решить через YQL)
  • Нетривиальная обработка ошибок транспорта
  • Для оперы понадобится дополнительный файл

Подробнее о GM_xmlHttpRequest


Полное описание объекта можно найти на сайте Greasespot.
Нам же необходимо знать, что GM_xmlHttpRequest является аналогом XMLHttpRequest и предоставляет такой же программный интерфейс.
Функция для посылки запроса может выглядеть так (применён хак setTimeout, чтобы обойти ошибку безопасности при обращении из небезопасного окна к коду юзерскрипта при выполнении запроса):

var GMTransport = function(url, onDone){
    setTimeout(function(){GM_xmlhttpRequest({
        method : "GET",
        url : url,
        onload : function(x) {
          var o = x.responseText;
          if (onDone) {
            onDone(o);
          }
        }
      });},0);
}



BeforeEvent


Это событие актуально только для Opera. Вызывается, когда в документе появляется элемент скрипта перед его выполнением. Событие можно перехватить и остановить выполнение скрипта. Чем мы и воспользуемся.

Важно: Для Opera нужно поставлять отдельный файл, в котором будет осуществляться навешивание события. Это связано с особеннностями выполнения пользовательских скриптов. Обратите внимание: в дополнительном скрипте не должно быть метаданных! Название скрипта начинается с нижнего подчеркивания, чтобы скрипт загружался первым.

Ссылка на дополнительный скрипт:_opera-xdr-engine.js
Код дополнительного скрипта на pastebin.com.

Запрос осуществляется вызовом следующей функции:

var scriptTransport = function(url, onDone){
    var t = document.createElement("script");
    t.src = url;
    t._callback = onDone;
    document.body.appendChild(t);
}



Замечание: Для Opera существует ещё одно решение: тыц. Я его не ковырял, но вероятно оно лучше представленного здесь.

JSONP


Реализация JSONP остаётся в качестве домашнего задания :)
Благо, работающих примеров в сети предостаточно.
Но прежде всего нужно задаться вопросом: нужна ли вам вообще поддержка вашего юзерскрипта в IE?

Разбираемся с Chrome


Для осуществления запросов в Хроме вам необходимо упаковать юзерскрипт в расширение.
Подробно данный метод описан в предыдущей статье. Там же дан пример проксирования кроссдоменного запроса (вызов функции запроса можно найти в оболочке, ниже по тексту).
Если лень переходить по ссылке вы читали статью и знаете, как упаковать скрипт, то код для background.html доступен на pastebin.

Оболочка


Все перечисленные выше способы собраны в оболочку.
Код не претендует на звание «лучший код 2011», но является рабочим и используется в различных модификациях в коммерческих юзерскриптах (да-да, бывают и такие).

Код оболочки можно найти на pastebin.com.
Кроссдоменный запрос осуществляется вызовом
xdr.xget(url, callback);


Определение нужного транспорта происходит автоматически.




  1. Учимся писать userscript'ы
  2. Userscripts. Углубляемся
  3. Userscripts. Упаковываем юзерскрипт для Chrome
  4. » Usersctripts. Кроссдоменные запросы
Tags:
Hubs:
+37
Comments 11
Comments Comments 11

Articles