четверг, 19 декабря 2013 г.

SOAP vs RESTful

Поучаствовал в бурных дебатах с коллегами на тему отличий SOAP- и REST-подходов. Вопрос довольно общий, возникает часто, поэтому я решил поделиться своим мнением в блоге.

Обычно при сравнении SOAP- и RESTful-сервисов заводят разговор о том, что мол "ну SOAP же тяжелый протокол, а REST-легкий, это всем известно". Но что тяжелого в SOAP-запросе? Пустой SOAP-запрос с заголовком выглядит следующим образом:


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
   </soapenv:Body>
</soapenv:Envelope>

Данный запрос занимает всего 158 байт. Говорить о том, что что-то тяжелое только потому что оно на 158 байт длиннее чего-то "легкого" несерьезно. Естественно, что различия между данными подходами гораздо глубже.


Принципиальное отличие между SOAP- и RESTful-сервисами заключается в том, что при SOAP-подходе мы оперируем множеством методов и у нас не существует понятия единого интерфейса, а в случае RESTful-подхода такой единый интерфейс есть и его методы однозначно отображаются на методы HTTP-протокола (в случае использования данного протокола в качестве транспорта). Речь идет о следующем. Интерфейс SOAP-сервиса описывается с помощью WSDL-документа. При этом у каждого сервиса он уникален. Интерфейс может содержать столько методов, сколько необходимо. Собственно пресловутая сложность использования SOAP заключается именно в этом: нужен WSDL, который к тому же необходимо разобрать и разработать обертки для всех его методов, способные интерпретировать SOAP-запросы и формировать SOAP-ответы. В случае же RESTful-подхода интерфейс всегда известен и описывается аббревиатурой CRUD - Create, Read, Update, Delete. При использовании в качестве транспорта HTTP данные операции отображаются на методы данного протокола: POST, GET, PUT, DELETE, соответственно. Сами же сущности, над которыми выполняются данные операции, однозначно идентифицируются с помощью Uniform Resource Identifiers (URI).

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

- getActualBalance(String number);
- widthraw(String number, Money amount);
- deposit(String number, Money amount).

При использовании же RESTful-сервисов ситуаций совершенно иная. У нас есть всего четыре операции над каждой сущностью: создать, изменить, получить и удалить. Применительно к банковскому счету мы можем запросить баланс с помощью операции GET, используя в качестве URI ссылку, содержащую идентификатор счета. Но у нас нет методов снять деньги со счета или внести деньги на счет. Впрочем, мы можем рассматривать данные операции как создание некой транзакции по перемещению денег. Пусть у нас будет сущность "транзакция", содержащая поля "номер счета" и "сумма", которая может быть отрицательная при снятии денег. Мы можем создавать данные транзакции с помощью метода POST. В итоге наш интерфейс будет выглядеть следующим образом:

- GET /bank/account?number=...;
- POST /bank/account/transactions.

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

Технология веб-сервисов, сердцем которой является SOAP, существует уже более десяти лет. За это время было разработано множество стандартов, существенно облегчающих жизнь разработчикам. Речь идет о так называемых WS-*-стандартах. Наиболее используемыми стандартами являются WS-Addressing, применяемый для разделения маршрутизации на транспортном и прикладном уровнях сетевой модели OSI и позволяющий строить асинхронные веб-сервисы. WS-Security - средство для декларативного управления безопасностью на уровне сообщений. С помощью данного стандарта можно настроить шифрование как всего сообщения, так и отдельных его частей, а так же вставить в него электронную цифровую подпись, тем самым гарантировать целостность и неотрекаемость. WS-ReliableMessaging - стандарт, обеспечивающий гарантированную доставку сообщений. Данный стандарт позволяет разработчикам абстрагироваться от работы с очередями сообщений, сконцентрировавшись на логике сервиса. WS-AtomicTransaction - стандарт, позволяющий осуществлять распределенные транзакции, в которых участвуют клиент и сервис. Распределенные транзакции позволяют обеспечить целостность данных в нескольких приложениях. Стоит отметить, что поддержка данных стандартов со стороны производителей программного обеспечения с каждым годом улучается, что является важным аргументом в пользу SOAP-сервисов.

Другой сильной стороной SOAP является независимость от транспорта. При интеграции корпоративных приложений успешно используется как SOAP over HTTP, так и SOAP over JMS.

Сильной стороной RESTful-сервисов является независимость от формы представления данных. Разработчик не связан необходимостью использовать SOAP-протокол и может выбрать тот формат передачи данных, который ему нужен. Не совсем верно передавать XML, если клиентом выступает браузер с поддержкой JavaScript. Гораздо лучше в таком случае передавать данные в формате JSON, который легко преобразуется в JavaScript-объекты путем вызова единственного метода eval(). Да и места данные в формате JSON занимают меньше, что при передаче по сети может быть важно.

Можно настроить RESTful-сервис таким образом, чтобы он оперировал данными в формате, требуемом для приложения. Многие популярные сервисы так и делают, например Twitter может отдавать данные как в XML, так и в JSON. Для указания требуемого формата используется URI, хотя это и не совсем верно с точки зрения архитектурного стиля REST - URI должен адресовать сам ресурс, а не служить для выбора формата его представления. При отправке HTTP-запроса для выбора предпочтительного формата представления можно использовать заголовок Accept.

Однако, за свободу в выборе формата представления данных приходится платить. Прежде всего сложностью в реализации стандартов для обеспечения безопасности, транзакционности, маршрутизации и гарантированной доставки. Все стандарты WS-* приспособлены для использования SOAP в качестве формата представления данных, поэтому для RESTful-сервисов приходится придумывать свои подходы. Работы в данном направлении идут, но кто знает, может быть со временем это сделает REST таким же "тяжелым" как SOAP? К тому же любая технология не лишена глобального недостатка: "это придумали не мы". Возможно уже завтра "тяжелым" объявят REST, а на смену ему придет новая серебряная пуля от маркетологов.

Понравилось сообщение - подпишитесь на блог

4 комментария:

Alexander Tonchev комментирует...

Несколько раз встречал мнение, что успешные технологии межплатформенного взаимодействия имеют в своей основе соглашение о данных (можно сказать, о "представлении данных", но это будет слишком явно в пользу RESTful) и не накладывают ограничений на API в виде обязательных абстракций. В пример обычно приводят HTTP. И CORBA как пример неудачного подхода. То есть очередная победа принципа "worse is better": на множестве платформ поддержка более простого протокола обеспечивается раньше и дешевле. Но в рамках одной платформы картина другая: например, JMS и JDBC. SOAP в этом смысле совсем не плох. Можно использовать минимум абстракций, а клиент может вообще не знать о SOAP, главное чтобы транспорт поддерживал.

Когда критики упоминают тяжесть SOAP, хотят они того, или нет, они говорят об имплементации. И это не только RPS в рантайме. Это еще и количество кода, который требует конкретный фреймворк; количество действий при деплое; количество пробелов в документации, которые разработчику приходится восполнять путем проб и ошибок. Скажем, Apache Axis довольно убого документирован, полагается на генераторы кода и требует особых артефактов при деплое. Учитывая, сколько Java разработчиков со времен EJB2 избегают JEE и соответственно, не в курсе развития JAX-WS, понятно, какая репутация могла сложиться у SOAP даже в мире Java. Что уж говорить про PHP и Python. Возможно, будущее SOAP ограничивается только JEE и .Net. Ничего страшного, ну, будет меньше упоминаний на Хабре.

Мне кажется, как в любом технологическом холиваре, вывод скучен: для каждой технологии есть своя ниша. Другое дело, что некоторые ниши сжимаются, некоторые пропадают, а вендоры (да и технологические консультанты, которых чаще нанимают уже после выбора вендора) экономически не заинтересованы в том, чтобы клиент мог сам оценить эффективность того или иного инструмента.

Небольшое замечание. eval() не подходит для анмаршалинга, так как JSON это не подмножество JS. Да и это было бы небезопасно. Есть стандартный объект JSON, но Internet Explorer его поддерживает с версии 8, что вносит свои аспекты в спор SOAP vs RESTful.

Pavel Samolisov комментирует...

Не соглашусь по поводу eval(), если мне не изменяет память, то уже в IE 6 и FF 3 этот метод работал. Насчет небезопасности, тот JavaScript-код, в котором вызывается eval() загружается с того же самого сайта, что и AJAX-ответ. В принципе вредоносный код может быть уже в нем самом. Что же касается кроссдоменного AJAX, то в IE 6 и 7 он запрещен.

ilinchik комментирует...

"Когда критики упоминают тяжесть SOAP, хотят они того, или нет, они говорят об имплементации." - полностью согласен, это дополнительная головная боль для клиентов вашего сервиса, WSDL, чего-то генерировать? что за хрень!? - GET /someData/data?id=... что может быть проще!

Павел, вы не упомянули вопросы безопасности, как на счет аутентификация в архитектуре REST? Что можно противопоставить WS-Security?

Pavel Samolisov комментирует...

>> GET /someData/data?id=... что может быть проще!

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

Не стоит так же забывать, что реализации то бывают разными! В OSB мы просто подключаем WSDL к прокси-сервису и идем писать XQuery-трансформации из XML в нужные нам форматы. В случае же REST и использования JSON для представления данных необходимо писать свои парсеры на Java, подключая различные библиотеки. Здесь уже справедлив вопрос: "Подключил WSDL, указал конечную точку, что может быть проще! А то брать Java, обходить JSON, формировать XMLObject и затем трансформировать, что за хрень, зачем все это". Это я пишу не с целью с вами поспорить, а просто описываю другой взгляд на проблему.

>> Павел, вы не упомянули вопросы безопасности, как на счет аутентификация в архитектуре REST? Что можно противопоставить WS-Security?

Почему же, я ведь написал про WS-Security. В архитектуре REST аутентификацию делают по-разному, насколько я знаю до сих пор идут споры о том, являются ли HTTP-сессии рест-кошерными или нет. Одни адепты говорят, что сессии это состояние, а в REST его быть не должно, другие же уповают на то, что информация о пользователе - это все же не состояние ресурса и использовать сессии для ее хранения можно. Другой путь - использовать некий хэш и передавать его при каждом запросе через Cookies, на сервере же его валидировать. Третий путь - передавать при каждом запросе логин и пароль.

Собственно одна из идей поста заключается в том, что за "GET /someData/data?id=..." скрывается как бы не больше нюансов, чем у старых-добрых SOAP веб-сервисов. Поэтому в каждом случае нужно опираться не на мнение очередного проповедника, а принимать решение самостоятельно, взвешивая все за и против, учитывая сильные и слабые стороны своей платформы.

Отправить комментарий

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!