
Увы, реальность за хабракатом меня сильно разочаровала — я увидел очередной велосипед, да еще и с квадратными колесами. (Коллеги, ничего личного, только техническое обсуждение.) Правда, авторы честно сказали, что увидели на нескольких сайтах модное слово REST и решили сделать по нему. Только вот поняли они этот «РЭСТ» по-своему, примерно как Дед Щукарь читал и понимал толковый словарь.
В этом топике я призываю по-настоящему покончить с велосипедами в API сайтов. Ведь получается какой анекдот: АПИ разрабатывается для упрощения доступа к сайту и легкости подключения внешних систем, а получается такой, что с ним еще сложнее, чем без него :)
Чуть ниже под катом я подпишу смертный приговор всем велосипедам в универсальных API. Чтобы не быть голословным, я все проиллюстрирую примерами.
Но должен предупредить сразу — после прочтения статьи вы не сможете без рвотного рефлекса смотреть на очередной велосипед Васи Пупкина под гордым названием «универсальное API сайта».
В повествовании будут рассмотрены следующие вопросы:
- Базовые технологии: XML-RPC, REST, SOAP и краткое сравнение
- Дао вебсервиса
- Просветленные API
- Как отличить сайтовое API от говна
- Выводы
Итак на сегодня наиболее распространенными способами доступа к 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
- <soapenv:Envelope
- xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/
schemas.xmlsoap.org/soap/envelope">
- <soapenv:Body>
- <req:echo xmlns:req="http://localhost:8080/axis2/services/MyService/">
- <req:category>classifieds</req:category>
- </req:echo>
- </soapenv:Body>
- </soapenv:Envelope>
и ответ:
Copy Source | Copy HTML
- <soapenv:Envelope
- xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/
schemas.xmlsoap.org/soap/envelope">
- <soapenv:Header>
- <wsa:ReplyTo>
- <wsa:Address>schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
- </wsa:ReplyTo>
- <wsa:From>
- <wsa:Address>localhost:8080/axis2/services/MyService</wsa:Address>
- </wsa:From>
- <wsa:MessageID>ECE5B3F187F29D28BC11433905662036</wsa:MessageID>
- </soapenv:Header>
- <soapenv:Body>
- <req:echo xmlns:req="http://localhost:8080/axis2/services/MyService/">
- <req:category>classifieds</req:category>
- </req:echo>
- </soapenv:Body>
- </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
- <pre>
- from ZSI.ServiceProxy import ServiceProxy # импортируем замечательную либу Zolera Soap Infrastructure
- api = ServiceProxy('http://webservices.aeroflot.ru/flightstatus.asmx?WSDL')
- # ... и все! API сайта полностью готово к работе.
- # наш объект api содержит методы
- 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.
Удачи и приятных интерфейсов!