Userscripts. Кроссдоменные запросы
JavaScript*
Доброго времени суток.
Сегодня мы рассмотрим варианты организации кроссдоменных запросов в юзерскриптах.
В подробности реализации того или иного механизма я вдаваться не буду, но приведу пример кроссбраузерной оболочки для кроссдоменных запросов.
Вступление
Как известно, в чистом виде в естественной среде браузерного окружения кроссдоменные запросы — штука не тривиальная (благодаря 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);
Определение нужного транспорта происходит автоматически.