Grabduck

Дао Вебсервиса. (Или да хватит же изобретать велосипеды!)

:

image Недавно на Хабре была опубликована статья под провокационным заголовком и призывом к прекращению изобретений велосипедов в API-строении. Поскольку тема мне интересна, то я просто не мог пройти мимо.
Увы, реальность за хабракатом меня сильно разочаровала — я увидел очередной велосипед, да еще и с квадратными колесами. (Коллеги, ничего личного, только техническое обсуждение.) Правда, авторы честно сказали, что увидели на нескольких сайтах модное слово REST и решили сделать по нему. Только вот поняли они этот «РЭСТ» по-своему, примерно как Дед Щукарь читал и понимал толковый словарь.
В этом топике я призываю по-настоящему покончить с велосипедами в API сайтов. Ведь получается какой анекдот: АПИ разрабатывается для упрощения доступа к сайту и легкости подключения внешних систем, а получается такой, что с ним еще сложнее, чем без него :)

Чуть ниже под катом я подпишу смертный приговор всем велосипедам в универсальных API. Чтобы не быть голословным, я все проиллюстрирую примерами.
Но должен предупредить сразу — после прочтения статьи вы не сможете без рвотного рефлекса смотреть на очередной велосипед Васи Пупкина под гордым названием «универсальное API сайта».

В повествовании будут рассмотрены следующие вопросы:

  1. Базовые технологии: XML-RPC, REST, SOAP и краткое сравнение
  2. Дао вебсервиса
  3. Просветленные API
  4. Как отличить сайтовое API от говна
  5. Выводы

Итак на сегодня наиболее распространенными способами доступа к API сайтов являются:
1. XML-RPC.
2. REST (с оговоркой, что это не протокол, а подход).
3. SOAP.

Базовые технологии и сравнение

XML-RPC

Думаю, не сильно ошибусь, если скажу, что XML-RPC — прародитель всей этой галиматьи, которую мы сейчас называем громкими словами «веб-сервисы» и «сайтовое api». Разрабатывался в 1998 году по заказу Микрософта. По своей сути это реинкарнация старого доброго RPC с использованием форматирования сообщений в XML. Несомненным преимуществом протокола является его простота, и как следствие, легкость реализации.
Вот пример типичного XML-RPC запроса:
<?xml version="1.0"?>
 <methodCall>
   <methodName>examples.getAirportCode</methodName>
   <params>
     <param>
         <value><i4>567</i4></value>
     </param>
   </params>
 </methodCall>

На что можно получить такой же простой ответ:
<?xml version="1.0"?>
 <methodResponse>
   <params>
     <param>
         <value><string>SVO</string></value>
     </param>
   </params>
 </methodResponse>


Примерно так же просто выглядят сообщения об ошибках. Такой ответ легко распарсить даже человеку, не говоря уже про машину. Такая простота сослужила ему двоякую службу: с одной стороны он не был принят заказчиком и не стал стандартом, а с другой — понравился толпе простых незамороченных программистов. Этим ребятам нужно было простое и надежное средство обмена информацией между системами, они не хотели заморачиваться на такую хрень как красивый УРЛ, схема документа и прочие академизмы. Первое, что они хотели получить — простую работающую систему. И до сих пор XML-RPC помогает им в этом.
Итак:
+ : простота, краткость сообщений, минимальная проверка формата данных,
— : недостаточная строгость, требуется отдельное описание сервиса.
REST

Наверное, Рой Филдинг был первым, кто сказал «хватит изобретать велосипеды» и «все уже украдено до нас» применительно к веб-сервисам. А может и не первым, в любом случае его слова прозвучали наиболее громко. В своей диссертации Architectural Styles and the Design of Network-based Software Architectures он описал базовые принципы REST (Representational State Transfer) — архитектура веб-сервисов, изменившая представления разработчиков на 180 градусов**. Он сказал, что «танцевать надо не от угла, а от печки» — иными словами — надо не процедуру вызывать и передавать ей объект, а обращаться к объекту. Не надо наворачивать велосипед на мопед. Ведь самом протоколе HTTP уже есть ряд методов работы с объектами: GET (получить), POST (отправить/создать), PUT (обновить), DELETE (удалить).
Зачем заниматься тавтологией и вызывать
POST /api/object.php?object_id=445&action=delete&user_id=255&auth_key=0Jf4tet5 HTTP/1.1

Когда можно просто:
DELETE /objects/445 HTTP/1.1

Или еще практикуется вариант, когда делается POST на сам ресурс, а delete передается отдельно параметром:
POST /objects/445 HTTP/1.1

При этом, если XML-RPC использует из HTTP-протокола только транспортную часть для передачи XML-ки запроса/ответа, то REST задействует HTTP по-полной: здесь и авторизационные заголовки, content-negotiation — предпочтения по формату, языку, кодировке и виду ответа, различные служебные заголовки, безгеморная передача бинарных данных и т.п. Ошибки хорошо описываются кодами HTTP 4xx и 5xx. Можно видеть, что REST — органичная надстройка над HTTP. Это неудивительно ведь Рой — один из разработчиков протокола HTTP. Вообще, чем больше я разбираюсь с этим протоколом тем больше мне кажется, что он опередил свое время лет на 20, и чем дальше развивается веб, тем больше возможностей мы из него используем.
Само тело сообщения может передаваться в разных форматах: классическом XML либо гиковском JSON. Вообще, REST это не протокол, это подход, и как раз здесь заключена его гибкость, он как бы говорит нам: «Ребята, берите базовые принципы, а дальше делайте как вам удобно». Близость к HTTP-протоколу упрощает и ускоряет его обработку веб-серверами.
Итак:
+ : гибкость, простота, скорость обработки (особенно важно для крупных сайтов), органичность протоколу, мультиформатность, компактность.
— : отсутствие строго контроля данных, из практических соображений приходится выходить за рамки идеальной модели.
SOAP

Вот мы и добрались до него — протокол-подарок! Но именно на нем я постиг дао всех веб-сервисов и сайтовых апи. Думаю, все в курсе, что значит аббревиатура SOAP — Simple Object Access Protocol. Именно так в представлении Микрософта должен выглядеть идеальный протокол для веб-сервисов. Давайте посмотрим на что похожи типовой запрос:
Copy Source | Copy HTML
  1. <soapenv:Envelope
  2.     xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.     xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/
    schemas.xmlsoap.org/soap/envelope">
  5.   <soapenv:Body>
  6.     <req:echo xmlns:req="http://localhost:8080/axis2/services/MyService/">
  7.       <req:category>classifieds</req:category>
  8.     </req:echo>
  9.   </soapenv:Body>
  10. </soapenv:Envelope>

и ответ:

Copy Source | Copy HTML
  1. <soapenv:Envelope
  2.     xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  3.     xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5.     xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/
    schemas.xmlsoap.org/soap/envelope">
  6.   <soapenv:Header>
  7.     <wsa:ReplyTo>
  8.       <wsa:Address>schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
  9.     </wsa:ReplyTo>
  10.     <wsa:From>
  11.       <wsa:Address>localhost:8080/axis2/services/MyService</wsa:Address>
  12.     </wsa:From>
  13.     <wsa:MessageID>ECE5B3F187F29D28BC11433905662036</wsa:MessageID>
  14.   </soapenv:Header>
  15.   <soapenv:Body>
  16.     <req:echo xmlns:req="http://localhost:8080/axis2/services/MyService/">
  17.       <req:category>classifieds</req:category>
  18.     </req:echo>
  19.   </soapenv:Body>
  20. </soapenv:Envelope>

— Фак май мозг! — воскликнете вы и будете абсолютно правы — Это что же, ради передачи фитюльки на 10 байт мне надо всю эту лабуду писать?
— Да — скажет Логика, и вы, засучив рукава пойдете учить всю эту кучу технологий.

— Сюда еще и XML Schema приплели зачем-то? Какого хрена? А вы уверены, что эти ребята правильно понимают смысл слова «Simple»? — такие мысли будут вас посещать, и это хорошо — вы на верном пути.

Но и это не все: чем больше копаешься в SOAP тем больше всяких разных технологий вылазит на тебя. Когда вы дойдете до WSDL (Язык описания веб-сервисов), вы поймете почему, начиная с какой-то версии, разработчики перестали воспринимать название SOAP как аббревиатуру, а начали понимать буквально — soap («мыло»). В этот момент ваши мысли будет занимать одна идея: почему во всем этом зоопарке технологий отсутствует технология rope («веревка»).

Еще через какое-то время вы воскликните: «Ну нафиг, давайте уж без меня: сами придумали — сами и возитесь. Я вам не машина мегабайты иксэмеля в мозгу парсить!».
Поздравляю: теперь вы постигли дао вебсервисов!

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

Просветленные API


API сайта и веб-сервисы — это не что-то милостиво спущенное на нас с небес создателями сайта! Это банальная библиотека функций, которую мы можем встроить в свою программу. Этот вывод становится совершенно естественным, когда вы начинаете мыслить глобально за пределами своего компьютера.
Если вам нужна новая уже готовая либа в проекте, что вы делаете? Скорее всего просто скачиваете, устанавливаете и используете? Пишете в начале программы use/require/import/include/… а дальше просто вызываете функции. Почему же работа с веб-библиотекой должна быть сложнее?
Вот теперь, просветлившись, мы можем начать работать с просветленным API.

Сейчас в качестве примера мы 1 минуту сделаем работу с API Аэрофлота, будем подглядывать за табло Шереметьево без отрыва задницы от стула. Я беру свой любимый язык и на нем пишу все примеры. Уверен, в вашем языке есть аналогичные модули. Ну а если их там нет, то самое время задуматься, так ли взаимна ваша любовь ;-)

Вот тут аэрофлот подробно расписывает свое чудесное API. На первый взгляд, описание достаточно убогое — чисто перечисление методов и параметров. А как же это вызывать, как парсить, что вообще делать? А это уже не наши заботы — пусть об этом болит голова у машины — она же железная, а свою мне не хочется грузить фуфлом. Поэтому моя задача найти машинночитаемое описание сервиса — тот самый WSDL и скормить его компу.

Copy Source | Copy HTML
              
     
     
          
     
  1. <pre>
  2. from ZSI.ServiceProxy import ServiceProxy #  импортируем замечательную либу Zolera Soap Infrastructure
  3. api = ServiceProxy('http://webservices.aeroflot.ru/flightstatus.asmx?WSDL')
  4. # ... и все! API сайта полностью готово к работе.
  5. # наш объект api содержит методы 
  6. AirportInfo() Departure()
  • AirportList()         FlightInfo()
  • Arrival()               FlightSearch()
  •  
  • # то есть все те, что описаны в доке. 
  • # Нам осталось лишь вызвать их с нужными параметрами:
  • airport_list = api.AirportList()
  • airport_list
  • {'AirportListResult': {'Airport': [{'city': 'Colombo',
  •                                     'code': 'CMB',
  •                                     'id_country': 'SRI',
  •                                     'name': 'Bandaranayake'},
  •                                    {'city': 'Belfast',
  •                                     'code': 'BFS',
  •                                     'id_country': 'UK',
  •                                     'name': 'Belfast Intl'},
  •                                     .....
  • # Обратите внимание насколько все просто. Мы в 2 команды подключились к API и легко вызываем его методы - сразу получаем результат отпарсенный и подготовленный к работе в нашем языке.
  • # Работать мне с ним так же легко и просто, словно это обычная библиотека на моем компе. 
    # Для моей программы это абсолютно прозрачно.
  • # Нас интересует табло прибытия за 15 число:
  • import datetime
  • date1 = datetime.date(2009, 11, 15)
  •  
  • arrival = api.Arrival(code='SVO', date=date1, order_field='airport', order='asc')
  • arrival
  • {'ArrivalResult': {'Flight': [{'airport': 'Adler/Sochi',
  •                                'calc': (1, 1, 1,  0,  0,  0,  0,  0,  0),
  •                                'company': 'SU',
  •                                'fact': (1, 1, 1,  0,  0,  0,  0,  0,  0),
  •                                'flight_no': '874',
  •                                'flt_pk': '2009101359805123',
  •                                'is_board':  0,
  •                                'is_check':  0,
  •                                'plan': (2009, 11, 15, 10, 55,  0,  0,  0,  0),
  •                                'real': (1, 1, 1,  0,  0,  0,  0,  0,  0),
  •                                'sched': (2009, 11, 15, 10, 55,  0,  0,  0,  0),
  •                                'status': ''},
  •                                ......
  • </pre>
  • Стойте-стойте, а как же вся эта лабуда с XML, REST, передачей параметров, парсингом ответов и всеми прочими атрибутами «крутого» API?
    Лучше всего на это отвечает фильм «Матрица», кадр из которого мне захотелось вынести в заголовок:

    — Нет никакой ложки, Нео.

    Только перестав думать о ложке сайтовом API как о чем-то реальном, сложном, можно начать гнуть ее комфортно использовать.
    Это и есть просветленное API — прозрачное и светлое настолько, что вы его не замечаете, когда работаете. Для вас это просто локальная библиотека. А вся остальная механика с сетевыми заморочками происходит где-то внутри и вас не напрягает.

    Как отличить сайтовое API от говна.


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

    Выводы


    В наше время наличие API для сайта претендующего на внимание программистов, всевозможные интеграции и прочее — не повод для гордости, а предмет первой необходимости. Но мало сделать API, даже если вы используете самые модные принципы — надо его сделать удобным и прозрачным. И технология передачи данных здесь имеет значение 10-й важности — это вообще не нужно прикладному программисту. Он должен просто взять и использовать вашу библиотеку.
    Если вы хотите получить его внимание, чтобы он потратил свое время на интеграцию с вами — сделайте первый шаг — потратьте время на него. Дайте ему очень простую и понятную библиотеку.
    Не надо кивать на то, что «мы сделали как твиттер — дали REST-подобный интерфейс». Вы забываете главное — у твиттера на каждый язык программирования по 5-10 библиотек, которые можно просто скачать и использовать не заморачиваясь на протокол rest/xmlrpc/soap.

    Удачи и приятных интерфейсов!