2. easyAPI. Удаленная загрузка XML.
И вот, наконец, долгожданная, вторая статья из раздела easyAPI.
Сегодня мы узнаем, как загрузить XML-данные с удаленного домена. Подобная задача возникает, например, когда владелец сайта на бесплатном (а значит – без серверного кода) хостинге хочет вставить в страницу погодный XML-информер. Именно на этом примере мы и рассмотрим сегодня удаленную загрузку XML-файлов.
Итак, для фоновой загрузки данных мы привыкли использовать так называемый AJAX, то есть объект XMLHttpRequest, про эту технологию я уже не раз писал в статьях блога. Однако, эта технология в данном случае никуда не годится. Почему? А потому что объект XMLHttpRequest не позволяет делать запросы к доменам, иным, чем тот, в котором работает скрипт, создающий этот объект.
И здесь к нам на помощь приходит технология, получившая название JSONP, что значит JavaScript Object Notation with Padding. Про сам JSON можео почитать вот здесь JSON, а JSONP представляет собой объект в нотации JSON обернутый в вызов функции, что-то вроде этого:
onSuccess('JSON-object')
А теперь объясню, зачем это нужно. Хоть XMLHttpRequest и не позволяет делать запросы к удаленным доменам, однако все браузеры позволяют нам загружать JavaScript’ы с любых доменов, то есть страница, загруженная с домена localdomain.ru, позволяет выполнить в ее рамках следующую инструкцию:
<script type='text/javascript' src='http://remotedomain.ru'> </script>
А теперь смотрите, что происходит. Наша страница загружает скрипт с удаленного домена, а скрипт этот возвращает нам JSONP-конструкцию, которая после загрузки скрипта тут же и выполнится, а значит, вызовет некую предопределенную заранее функцию onSuccess, которая получит в качестве аргумента тот самый XML-файл. И теперь мы можем делать с ним все, что захотим!
Осталось только одно – завладеть таким remotedomain.ru, который вернет нам то, что нам нужно. Скажу, что домен этот – http://easyapi.ru. Да-да, именно на этом, предусмотрительно приобретенном не так давно домене, и будут жить все наши полезные, жизненно необходимые почти каждому веб-разработчику, функции. 🙂 Скромненько и со вкусом…
Итак, в рамках домена easyapi.ru я уже создал ответную часть для удаленной загрузки XML-документов. Живет она вот здесь: http://easyapi.ru/xml/get.php и принимает на входе два GET-параметра: url и callback. Первый из них – адрес, с которого будет загружаться XML-файл, а второй – имя JavaScript-функции, которая будет вызвана после загрузки, и которая должна будет обработать полученный XML-документ.
Не откладывая дело в долгий ящик, приведу ниже содержимое страницы, которая, используя наш сервис easyAPI, загружает данные об XML-погоде в городе Москва и при помощи XSLT-шаблона преобразует эти данные в красивый информер, который вы, кстати, изучив основы XSLT-преобразований, сможете оформить по своему усмотрению.
Вот исходный код страницы:
<html> <head> <script type="text/javascript"> function getXMLFromString(s) { if(window.ActiveXObject) { var xml; xml=new ActiveXObject("Microsoft.XMLDOM"); xml.async=false; xml.loadXML(s); return xml; } else if(window.DOMParser) { var parser = new DOMParser(); return parser.parseFromString(s,'text/xml'); } else { alert("Загрузка XML не поддерживается браузером"); return null; } } function getXMLDocument(url) { var xml; if(window.XMLHttpRequest) { xml=new window.XMLHttpRequest(); xml.open("GET", url, false); xml.send(""); return xml.responseXML; } else if(window.ActiveXObject) { xml=new ActiveXObject("Microsoft.XMLDOM"); xml.async=false; xml.load(url); return xml; } else { alert("XML loading not supported"); return null; } } function transformXslt(source,style) { if(window.ActiveXObject) { return source.transformNode(style); } else if(window.XSLTProcessor) { var xsltProcessor=new XSLTProcessor(); xsltProcessor.importStylesheet(style); var resultDocument = xsltProcessor.transformToDocument(source); var xmls = new XMLSerializer(); return xmls.serializeToString(resultDocument); } else { alert("Преобразование XML не поддерживается браузером"); return null; } } </script> </head> <body> <div id="res"></div> <script type="text/javascript"> function onSuccess(res) { var xslt = getXMLDocument("http://easy-4-web.ru/samples/easyxml/gis.xsl"); var xml = getXMLFromString(res.result); var res = transformXslt(xml, xslt); document.getElementById("res").innerHTML=res; } </script> <script src="http://easyapi.ru/xml/get.php?url=http://informer.gismeteo.ru/xml/27612_1.xml&callback=onSuccess" type="text/javascript"></script> </body> </html>
В первой части страницы мы видим три функции, все они – кроссбарузерные:
- getXMLFromString(s) – преобразует валидную строку s в XML-документ
- getXMLDocument(url) – загружает XML-документ с адреса url
- transformXslt(source,style) – преобразует XML-документ source при помощи XSLT-шаблона style
В нижней части исходного кода мы видим функцию onSuccess – ока как раз и выполнится после загрузки xml-документа с удаленного домена. Она отрисует загруженный и полученный в переменной res.result XML-Документ при помощи XSLT-файла, хранящегося по адресу http://easy-4-web.ru/samples/easyxml/gis.xsl
И в самой-самой нижней части исходного кода мы видим вот такую конструкцию:
<script src="http://easyapi.ru/xml/get.php?url=http://informer.gismeteo.ru/xml/27612_1.xml&callback=onSuccess" type="text/javascript"></script>
Вот эта часть и представляет собой загружалку XML-документов с удаленных доменов. Как видим в атрибуте src тега SCRIPT мы указали, кто будет заниматься загрузкой (http://easyapi.ru/xml/get.php), что он будет загружать (url=http://informer.gismeteo.ru/xml/27612_1.xml) и какая функция займется обработкой документа (callback=onSuccess)
Здесь вы можете скачать исходные коды приведенного примера. А вот здесь – посмотреть, как это работает.
Кросдоменный JavaScript (JSONP)
Итак, мы уже научились использовать объект XMLHttpRequest для фоновой загрузки данных с сервера, этот подход подробно описан в статье AJAX. 1 – Что это такое?. Однако, приведенный метод обладает одним недостатком – он не позволяет делать кроссдоменные запросы, то есть скрипт расположенный на домене easy-4-web.ru не может обратиться к домену vision4web.ru, а иногда этого так хочется. И здесь нам на помощь придет метод динамически подгружаемых скриптов <SCRIPT>.
Как известно, HTML-элемент <SCRIPT> может загружаться и с чужого домена, то есть аттрибут src у него может указывать вообще на любой домен. При этом, сразу после загрузки в нашу страницу, скрипт начнет выполняться. Таким образом, вставив в этот скрипт вызов некой заранее известной функции, мы можем организовать оповещение страницы о том, что скрипт загружен и готов к выполнению работы. А данные для для скрипта можно упаковать в пакет JSON. Такой комбинированный подход носит имя JSONP (JSON with padding).
На клиентской стороне мы выполняем такой код:
var script = document.createElement("script"); script.src = 'http://mydomain.ru/script.php?callback=func1'; script.type = 'text/javascript'; document.body.appendChild(script);
А по адресу http://mydomain.ru/script.php поселим вот такой код:
<?php echo($_REQUEST['callback'].'({"result":"Успешно!"})'); ?>
На клиентской стороне также существует такой callback (то есть функция, которая сработает после загрузки скрипта):
function func1(response) { alert(response.result); }
Протестировать рабочий пример можно здесь: http://easy-4-web.ru/samples/jsonp/index.html, а скачать код вышеприведенного примера можно здесь: http://easy-4-web.ru/images/jsonp.zip
В тестовом примере, как можно увидеть, не происходит кроссдоменной загрузки – мы загружаем с того же домена, где расположен скрипт. Но вы можете заменить URL в этом месте кода:
<input type="button" value="Получить JSONP" onclick="getJSONP('http://easy-4-web.ru/samples/jsonp/handler.php',onSuccess)"/>
на ваш собственный или на наш тестовый handler, расположенный на другом домене, (вот здесь http://vision4web.ru/api/handler.php) и убедиться в том, что кроссдоменная загрузка прекрасно работает.
Ниже приведен скриншот сетевого взаимодействия:
Как видно, при нажатии на кнопку “Получить JSONP” происходит создание элемента <SCRIPT> с атрибутом src=http://easy-4-web.ru/samples/jsonp/handler.php, при этом браузер тут же начинает загружать этот скрипт с удаленного сервера. А загруженный скрипт представляет собой вызов функции onSuccess() с параметром {“result”:”Успешно!”}. После загрузки скрипта он тут же начинает выполняться, тем самым выполняя функцию OnSuccess, которая, получив в качестве аргумента JSON-объект, обрабатывает его и совершает все необходимые действия, обусловленные логикой приложения.
Этот подход я собираюсь использовать при реализации многих полезных сервисов в рамках проекта easyAPI.