Создаем собственный API

Кирилл Евсеев, February 5, 2011

Введение

Всем привет. Сегодня я начну цикл статей, посвящённых созданию API.

Сразу, несколько ответов на ваши невысказанные вопросы:

  1. Цикл, потому что материал достаточно объёмный и серьёзный. В одну статью не вместится.
  2. API (application programming interface, в переводе с буржуйского – интерфейс программирования приложений) используется в том случае, если необходимо предоставить сторонним разработчикам часть низкоуровневого функционала некоторой системы.
  3. Класс задач, в которых используется API, разнообразен. Вот несколько примеров: вы являетесь владельцем базы данных всех жителей страны и хотите предоставить возможность различным компаниям через их корпоративные приложения пользоваться вашей базой. В этом случае, вам необходим API, т.к. просто продать базу данных экономически невыгодно и несёт в себе риск по утечке информации. Другой пример: у вас есть некоторая мультифункциональая система и вы хотите предоставить её часть для эволюции другим программистам. Так делает, например, Vkontakte. (more…)

AJAX. Загрузка файлов.

Вячеслав Гринин, February 20, 2010

Немного странное название статьи… Мы помним, что AJAX это асинхронный JavaScript, построенный на основе объекта XmlHttpRequest. Отличная технология! С некоторыми ограничениями. Одно из них – отсутствие кросдоменности – мы уже научились обходить в статье Кросдоменный JavaScript (JSONP). Но есть у этой замечательной технологии еще один недостаток – она не позволяет асинхронно загружать файлы. Ну вот не умеет объект XmlHttpRequest загружать файлы!

Как обойти это ограничение? Как оказалось – очень просто. Правда это уже не будет AJAX, но это все еще будет асинхронный запрос, то есть при загрузке файла нам не придется перезагружать саму страницу с формой, загрузка произойдет в фоновом режиме. Да еще и вернет нам результат операции в виде произвольного объекта, полностью описывающего результат загрузки. Это очень хорошо.

Для загрузки файлов мы используем технологию, которая называется Remote Scripting посредством IFRAME. А в общем, технология проста. Для загрузки файла мы все также будем использовать обычную форму FORM, в которой разместим самый обычный INPUT TYPE=’FILE’. Все как и прежде в случае синхронной загрузки файла. И вот здесь и начинаются отличия. А отличие состоит в том, что у формы target указывает на скрытый на странице IFRAME, который по сути и перезагрузится во время операции. Мало того, в IFRAME мы вернем результат операции в виде вызова простого JavaScript-коллбэка, который завершит операцию загрузки.

Итак, от слов к делу. Далее я приведу пример кода, который и реализует вышеописанную технологию. Код этот очень простой.

<html>
<head>
<script type="text/javascript">
 function onResponse(d) {
 eval('var obj = ' + d + ';');
 alert('Файл ' + obj.filename + (obj.success ? " " : " НЕ ") + 
    "загружен.");
 }
 </script>
</head>

<iframe id="rFrame" name="rFrame" style="display: none">
</iframe>

<form action="handler.php" target="rFrame" method="POST" 
   enctype="multipart/form-data">
<input type="file" name="loadfile">
<input type='submit' value='Загрузить'>
</form>

</html>

Здесь мы видим функцию onResponse, которая получает результат обработки файла(результат этот мы формируем на сервере). Еще мы видим IFRAME с идентификатором rFrame, про который мы говорили выше. И видим мы также саму форму загрузки файла.

У формы action=”handler.php” указывает на серверный обработчик, и не забудьте указать enctype=”multipart/form-data” иначе форма не сможет загружать файлы.

А дальше я приведу серверный код, содержимое файла handler.php.

<?php
 function jsOnResponse($obj)
 {
 echo '
 <script type="text/javascript">
 window.parent.onResponse("'.$obj.'");
 </script>
 ';
 }

 $dir = '/home/path/path/path';
 $name = basename($_FILES['loadfile']['name']);
 $file = $dir . $name;

 $success = move_uploaded_file($_FILES['loadfile']['tmp_name'], $file);
 jsOnResponse("{'filename':'" . $name . "', 'success':'" . $success . "'}");

?>

Этот код просто сохраняет файл в папку на сервере, объяснять саму загрузку нет смысла – она стандартна. Обращу лишь внимание, что после загрузки файла вызывается функция jsOnResponse которая просто пишет в выходной поток вызов JavaScript-функции window.parent.onResponse($obj). Клиентский IFRAME, приняв этот код, выполнит его, вызвав функцию onResponse у родительской страницы фрейма.

Вот и все. Пользуйтесь. Здесь можно скачать работающий код приведенного в статье примера.

Используя уникальную систему поиска Price-AZ можно заказать любые запчасти Фольксваген и Ауди на сайте ООО “Польга”. Теперь стало гораздо удобнее найти нужную запчасть не только по номеру, но и по ее названию. При магазине есть также удобный автосервис. Кстати, система Price-AZ позволяет найти запчасть на автомобиль любой марки.

Приятный и удобный интернет магазин в котором есть все: от бытовой техники, до фототехники и телефонов. Захотел даже купить себе оригинальные весы подсказывающие цветом индикации динамику изменения моего веса.

Кросдоменный JavaScript (JSONP)

Вячеслав Гринин, October 10, 2009

Итак, мы уже научились использовать объект 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) и убедиться в том, что кроссдоменная загрузка прекрасно работает.

Ниже приведен скриншот сетевого взаимодействия:
screen

Как видно, при нажатии на кнопку “Получить JSONP” происходит создание элемента <SCRIPT> с атрибутом src=http://easy-4-web.ru/samples/jsonp/handler.php, при этом браузер тут же начинает загружать этот скрипт с удаленного сервера. А загруженный скрипт представляет собой вызов функции onSuccess() с параметром {“result”:”Успешно!”}. После загрузки скрипта он тут же начинает выполняться, тем самым выполняя функцию OnSuccess, которая, получив в качестве аргумента JSON-объект, обрабатывает его и совершает все необходимые действия, обусловленные логикой приложения.

Этот подход я собираюсь использовать при реализации многих полезных сервисов в рамках проекта easyAPI.

Что такое PEAR и как его установить?

Вячеслав Гринин, September 18, 2009

PEAR – это аббревиатура для “PHP Extension and Application Repository“.
PEAR это:

  • Структурированная библиотека открытого кода для пользователей PHP.
  • Система распространения кода.
  • Стандарт стиля написания PHP-кода, определенный здесь.
  • Библиотека PECL(PHP Extension Community Library)

Цель PEAR состоит в предоставлении компонентов многократного использования.
Код PEAR разделен на  “пакеты“. Каждый пакет – это отдельный проект со своей собственной командой разработки, версией, документацией и определенными связями и зависимостями с другими пакетами. Пакеты распространяются в виде gzip-файлов с файлом описания внутри, и устанавливаются при помощи инсталлятора PEAR.
Все пакеты PEAR зарегистрированы на центрельном сервере pear.php.net и загружаются с него же.
Сторонние серверы, называемые “каналами”, также распространяют пакеты, которые могут быть установлены инсталлятором PEAR.
PECL является отдельным проектом распространения расширений PHP (оттранслированных программ на C). Расширения PECL также распространятся в виде пакетов и могут быть установлены посредством инсталлятора PEAR.

Установка PEAR.
Для Windows.
1) Сначала устанавливаем Джентльменский Набор Web-Разработчика. Это очень полезный пакет, который за несколько минут установит на вашем компьютере web-сервер Apache и PHP.
2) В папке z:/home/ инсталляционной директории denwer-а создаем папку myhost (которая будет представлять “доменное имя” нашего локального сайта), а в ней – папку www. Далее я буду исходить из того, что ваш denwer функционирует под псевдонимом диска Z:
3) Идем на http://pear.php.net/go-pear. Это исходник инсталлятора, который установит PEAR на ваш компьютер. Сохраняем его в папку z:/home/myhost/www под именем go-pear.php
4) Запускаем в браузере http://myhost/go-pear.php и проходим весь путь инсталляции. Собственно, там нет ничего сложного, в основном приходится кликать на ссылку далее(кроме параметров конфигурации, смотрите ниже особенности их настройки). На последнем этапе установки мы увидим прогресс-бар, показывающий ход загрузки файлов на ваш компьютер.
5) открываем php.ini(у меня он лежит вот здесь Z:\usr\local\php5\) находим строку include_path и правим ее вот так include_path = “.;Z:\home\myhost\www\PEAR”
6) Рестартанем denwer И вот PEAR в нашем распоряжении

Проверим, работает ли PEAR.
В директории PEAR создадим файл check.php с содержимым:

<?php
require_once "System.php";
var_dump(class_exists("System"));
?>

И вызовем в браузере http://myhost/PEAR/check.php/. И если мы увидим bool(true) значит PEAR установился правильно.

Установка на UNIX-хостинг не отличается новизной, дл этого достаточно залить на хостинг файл go-pear.php, запустить его из браузера и пройти через все шаги инсталляции.

Теперь, запросив в браузере путь http://myhost/index.php мы попадем в веб-консоль управления пакетами PEAR. Она позволяет нам устанавливать в PEAR новые пакеты и конфигурировать сам PEAR. В нем также есть удобная система поиска пакетов по ключевым словам или по названию пакета.

И для тех, кто уже установил PEAR на хостинге, и теперь он доступен всем и каждому, дам совет защитить директорию с инсталляцией PEAR. Эта процедура подробно описана в статье Защита сайта с помощью .htaccess и .htpasswd.

UPD: К сожалению, инсталлятор слегка недоделан и поэтому значения по умолчанию в нем не всегда корректны. Из-за чего и возникают проблемы при установке под Windows. На втором шаге инсталлятора необходимо подкорректировать параметры установки, по умолчанию инсталлятор подставляет неправильные слеши и нам требуется поменять их.

Обратите внимание на 2, 4, 8 и 10 параметры, в них необходимо поменять слеши на другие.

UPD: У тех, кто устанавливает PEAR под Windows часто при вызове index.php возникает ошибка Fatal error: No PEAR.php in supplied PEAR directory: in Z:\home\localhost\www\index.php on line 24. Расскажу, как с ней бороться. Возникает она потому что инсталлятор не прописывает в файл index.php все необходимые пути, что не дает ему запустить Web Frontend, хотя сам PEAR при этом – работоспособен! Чтобы избавиться от этой ошибки и запустить Web Frontend Нужно исправить в index.php два параметра:
1) $pear_dir = ‘Z:\home\myhost\www\PEAR’;
2) $pear_user_config = ‘Z:\home\myhost\www\pear.conf’;
Web-сервер запущенный из-под Denwer не может найти файл pear.conf содержащий в себе все те параметры, что мы ввели на шаге конфигурирования инсталлятора PEAR (смотрите рисунок выше), и параметром $pear_user_config мы указываем ему точное расположение файла конфигурации. Ну и по непонятным мне причинам (наверное, инсталлятору не положено заниматься этой черной работой :)) здесь не прописан параметр $pear_dir, указывающий на директорию инсталляции PEAR. После исправления этих параметров ваш PEAR и Web Frontend запустятся. Теперь вы можете приступать к конфигурированию дополнительных пакетов PEAR.

Парсинг XML в JavaScript на примере XML-погоды от gismeteo.ru

Вячеслав Гринин, March 20, 2009

Итак, заинтересовала меня тема, как распарсить XML-файл посредством JavaScript. Немного порыскав в поисковиках наткнулся на весьма познавательную статью по этой теме XML в Microsoft Internet Explorer 5.0. Есть у этой статьи маленький недостаток – она посвящена только IE, а мы в нашем блоге твердо решили писать только кроссбраузерный код. Однако, для начала хватило и этой статьи, сначала весь код был написан для IE, и затем была осуществлена безуспешная попытка запустить его также в Opera, Mozilla, Chrome. Но как я уже говорил “не боги горшки обжигают”, все некроссбраузерные баги были успешно найдены и исправлены. Это р-раз.

В качестве подопытного кролика был взят XML-файл, содержащий в себе данные о погоде на ближайшие сутки от Gismeteo.ru. Собственно, открыв эту страницу, и выбрав интересующий вас город, вы можете ниже на той же странице наблюдать содержимое этого XML-файла, и описание его структуры. Это д-два.

Сопоставив первые два пункта уже можно сваять свой собственный парсер XML-документов на JavaScript, однако если вам лень, то читайте статью дальше. В ней я расскажу о том, как провести анализ содержимого XML-файла и отобразить его в том виде, в котором вам хочется. Это т-три.

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

Сразу хочу предупредить, что ниже пойдет теоретическая и практическая часть работы с XML-файлами, и если вам не хочется вникать во все эти подробности, то вы можете сразу перейти в самый конец статьи и по предложенной там ссылке скачать готовый скрипт обработки XML-документа, содержащего прогноз погоды от gismeteo.ru. А теперь теория…

Итак, что же такое XML? Wikipedia утверждает, что:

XML (eXtensible Markup Language — расширяемый язык разметки) — рекомендованный консорциумом W3C язык разметки, представляющий собой свод общих синтаксических правил. XML — текстовый формат, предназначенный для хранения структурированных данных, для обмена информацией между программами, а также для создания на его основе более специализированных языков разметки. Целью создания XML было обеспечение совместимости при передаче структурированных данных между разными системами обработки информации, особенно при передаче таких данных через Интернет.

Визуально структура XML может быть представлена как дерево. Важнейшее обязательное требование состоит в том, что документ должен иметь только один корневой элемент (в нашем случае это элемент MMWEATHER). Это значит, что данные всего документа должны быть расположены между единственным начальным корневым тегом и соответствующим ему конечным тегом.

Остальная часть XML-документа состоит из вложенных элементов, которые могут иметь атрибуты и содержимое. Элемент обычно состоит из пары тегов (открывающего и закрывающего), обрамляющих другие элементы. Открывающий тег состоит из имени элемента в угловых скобках, например, <FORECAST> ; закрывающий тег состоит из того же имени в угловых скобках, но перед именем ещё добавляется косая черта, например, </FORECAST>. Содержимым элемента называется всё, что расположено между открывающим и закрывающим тегами, включая текст и другие вложенные элементы.

Кроме содержания у элемента могут быть атрибуты — пары имя-значение, добавляемые в открывающий тег после названия элемента. Например в случае <PHENOMENA cloudiness=”2″ precipitation=”10″ rpower=”0″ spower=”0″/>
элемент PHENOMENA имеет 4 атрибута cloudiness, precipitation, rpower и spower имеющие соответственно значения 2, 10, 0, 0.

Итак, со структурой XML-файла более-менее разобрались. Осталось только понять, каким образом все это богатство обрабатывать в JavaScript. Собственно, в статье XML в Microsoft Internet Explorer 5.0 можно обо всем этом прочесть, но я намерен вкратце обрисовать ниже основные приемы анализа XML-файлов в контексте поставленной в начале статьи задачи – разбора XML-документа, содержащего прогноз погоды на сутки от gismeteo.

  1. Загрузка XML-документа. Хочу сразу заметить, что загрузка любых файлов из JavaScript возможна при соблюдении одного обязательного условия – загружаемый файл должен быть расположен в том же домене, где и сама страница его загружающая. Поэтому невозможно со страницы, расположенной, например, по адресу http://easy-4-web.ru/samples/gismeteo/ загрузить файл с сайта http://gismeteo.ru. А это означает, что … проблему надо как-то решать. Есть два пути: либо скачать файл с сайта gismeteo.ru на свой домен, либо создать на вашем домене простой PHP-обработчик, который сам будет скачивать файл с другого домена и выдавать его содержимое от своего имени. В предлагаемом мною решении я пошел по второму пути и поэтому чуть ниже приведу вам PHP-скрипт, выполняющий данное действие. А теперь, собственно, о загрузке XML-документов.
    В IE эта операция производится с помощью следующего кода

    xml=new ActiveXObject("Microsoft.XMLDOM");
    xml.async=false;
    xml.load(url);

    После выполнения которого в переменной xml будет храниться обработанный XML-Документ. Конечно, если документ был верно отформатирован. В ином случае будет сгенерировано исключение.
    Во всех остальных браузерах (Opera, Firefox, Chrome) загрузка файлов выглядит иначе:

    xml=new window.XMLHttpRequest();
    xml.open("GET", url, false);
    xml.send("");

    После чего в поле xml.responseXML можно обнаружить обработанный XML-документ. Таким образом, полный текст функции, осуществляющей загрузку XML-документа, будет выглядеть следующим образом:

    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 не поддерживается браузером");
                return null;
            }
    }
  2. Получение коллекции элементов по имени элемента производится использованием метода getElementsByTagName(tagname) объекта XMLDOMDocument. Например, получить массив всех элементов TOWN можно так: xml.getElementsByTagName(“TOWN”). Понятно, что в переменную xml уже должен быть загружен сам XML-документ.
  3. Просмотр коллекции элементов лучше проводить с помощью цикла for, так как итератор nextNode() пригодный для использования в цикле while, увы, не определен для результата, возвращаемого объектом XMLHttpRequest, а значит будет работать только в IE. Вот код для просмотра коллекции элементов циклом for:
    var towns=xml.getElementsByTagName("TOWN");
    if(towns)
    for(var i1=0; i1<towns.length; i1++)
    {
       town=towns[i1];
       // операции над текущим элементом town
    }
  4. Получение атрибутов элемента.
    Вообще, все атрибуты элемента хранятся в его коллекции attributes, однако доступ к ней неудобен, и поэтому я написал простую функцию, которая пробегает по всей коллекции атрибутов и распаковывает ее в удобный для работы массив, позволяющий получить доступ к любому атрибуту через оператор “квадратные скобки”. Вот эта функция:

    function getAttributes(node)
    {
      var ret = new Object();
      if(node.attributes)
      for(var i=0; i<node.attributes.length; i++)
      {
        var attr = node.attributes[i];
        ret[attr.name] = attr.value;
      }
      return ret;
    }

Вот собственно и вся предварительная подготовка. Используя эту информацию, уже можно написать анализатор XML-файлов с прогнозом погоды. Конечно, работа с XML-файлами гораздо сложнее, чем я здесь описал, но для пытливых умов существуют книги, интернет, поисковики.

Вот ссылка на готовый скрипт парсера XML-погоды. Вам следует только обратить внимание на шаблоны (переменная template) в конце файла index.html. Это объект в JSON-нотации, содержащий в себе следующие поля: “town”, “forecast”, “phenomena”, “pressure”, “temperature”, “wind”, “relwet”, “heat”, “t_forecast”, “t_template”. Первые 8 представляют собой строки, в которых хранятся шаблоны отображения конкретных нюансов прогноза: город, дата прогноза, атмосферные явления, давление, температура, ветер, относительную влажность, комфортную температуру. В HTML-разметку каждого шаблона вставляются конкретные данные прогноза, которые имеют вид соответствующих имен атрибутов, взятых в фигурные скобки. Все имена атрибутов вы можете увидеть внизу страницы Gismeteo.ru. Для некоторых числовых атрибутов существуют также их строковые псевдонимы, получаются они путем прибавления к атрибуту префикса “s”. Абсолютно все используемые в шаблонах атрибуты вы можете увидеть в конце HTML-разметки страницы index.html, в тегах <script>.

И еще два шаблона, используемых в переменной template. Это t_forecast и t_template. Первый задает разметку прогноза для конкретного времени суток, а второй – разметку для всех прогнозов одного города.

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

Где-то в середине статьи я обещал показать код PHP-Обработчика, скачивающего XML с других сайтов. Код этот вы можете увидеть в архиве с исходниками в файле xml.php. Он короткий и в пояснениях не нуждается.

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: