пятница, 28 февраля 2014 г.

Let's have a REST, часть I

За аббревиатурой REST (REpresentational State Transfer) стоит модель клиент-серверного приложения, в котором клиент и сервер общаются по stateless протоколу, и программный интерфейс между ними построен с учетом нескольких архитектурных требований.

REST не является технологией, в отличие от SOAP или HTTP. Поэтому нельзя сказать, что приложение использует REST. Приложение может соответствовать модели REST, и тогда говорят, что оно RESTful.

Среди требований к архитектуре RESTful приложения следующие:

  1. идентификация серверных ресурсов,
  2. манипуляция ресурсами с помощью представлений (representations),
  3. самоописательность клиентских запросов,
  4. гиперсcылки в представлениях для перехода к новым состояниям приложения.

Рассмотрим эти требования одно за другим.

В RESTful приложении ресурс - это некоторое понятие предметной области, идентифицируемое адресом. Ресурсы не равнозначны сущностям предметной области: разные ресурсы могут отображаться на одну и ту же сущность. Например, ресурсы "книга, которую читает мой друг", "последняя изданная книга Пелевина" и "книга под номером 1033 в библиотеке" могут отображаться на одну и ту же сущность. Каждый ресурс имеет собственный адрес, по которому он доступен клиенту:

/users/7/the_book_being_readкнига, которую читает мой друг
/authors/11/recently_published_bookпоследняя изданная книга Пелевина
/books/1033книга под номером 1033 в коллекции книг

В общем случае, с течением времени отображение ресурсов на сущности изменяется.

В RESTful приложении состояние ресурсов описывается представлениями, которые передаются между сервером и клиентом. (Это могут быть HTML, XML, JSON, CSV и другие представления.) Модель REST ограничивает запросы клиента к серверу четырьмя типами:

GETполучить представление ресурса
POSTсоздать ресурс согласно представлению от клиента
PUTизменить ресурс согласно предствалению от клиента
DELETEудалить ресурс

Так, для получения информации о ресурсе /books/1033 клиент отправляет запрос GET по адресу ресурса. В ответ сервер формирует представление, соответствующее запросу, и возвращает его клиенту. Чтобы изменить ресурс /books/1033, клиент формирует представление, содержащее изменения, и отправляет его в запросе PUT по адресу ресурса. Обрабатывая запрос, сервер изменяет ресурс.

Методы RESTful приложения GET, POST, PUT и DELETE не случайно имеют те же имена и семантику, что и методы протокола HTTP. Автором модели REST является один из разработчиков протокола HTTP Рой Филдинг, а RESTful приложения очень естественно реализуются на протоколе HTTP.

Требование самоописательности запросов означает, что все данные, необходимые для выбора обработчика запроса на сервере, должны содержаться в заголовке запроса или в метаданных. Идея в том, что не нужно читать письмо (тело запроса), чтобы доставить его адресату; достаточно прочитать адрес на конверте. В отличие от приложений, использующих технологии XML-RPC или SOAP, в RESTful приложении для диспетчерезации запроса на сервере не нужно анализировать тело запроса! Достаточно знать адрес ресурса и тип запроса.

Пример диспетчеризации запросов по адресу ресурса и типу запроса:

/booksGETобработчик для получения представления списка книг
/booksPOSTобработчик, создающий новую книгу из представления от клиента
/books/1033GETобработчик для получения представления книги
/books/1033PUTобработчик для изменения книги согласно представлению от клиента
/books/1033DELETEобработчик для удаления книги

Наконец, последнее из требований, которым должно удовлетворять RESTful приложение, состоит в том, что ответ сервера клиенту должен содержать гиперссылки для перехода к другим состояниям приложения. Тем самым, приложение через передаваемые клиенту представления информирует его о своих ресурсах, о которых клиент, до получения представления, мог и не знать. (В случае HTML-представлений, это гиперссылки для перехода к другим HTML-страницам.)

Например, представление списка книг, полученное в ответ на запрос GET, может содержать гиперссылки для перехода к следующим состояниям:

  • просмотр детальной информации о книге (для каждой книги в списке),
  • редактирование информации о книге (для каждой книги в списке),
  • добавление новой книги в библиотеку.

Итак, RESTful приложения манипулируют состоянием ресурсов, хранимых на сервере, посредством передачи представлений ресурсов в составе стандартизированных сообщений (запросов и ответов). Если приложения, использующие XML-RPC или SOAP, имеют программный интерфейс со множеством произвольно названных методов, то RESTful приложения используют только четыре метода: GET, POST, PUT и DELETE. Проектирование RESTful приложения, по сути, сводится к определению списка ресурсов и принятию решений для каждого ресурса, какие из четырех методов будут с ним использоваться.

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

учитывать книги
создать учетную запись книги
изменить учетную запись книги
хранить книги
поставить книгу на полку
снять с полки
переставить с полки на полку
читать книги
начать читать книгу
закончить читать книгу
списывать книги.

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

Идентифицируем ресурсы:

/booksкниги
/books/<id>книга
/shelvesместа хранения
/shelves/<id>место хранения
/placesфакты хранения
/places/<id>факт хранения
/usersчитатели
/users/<id>читатель
/readsфакты чтения
/reads/<id>факт чтения
/writeoffsфакты списания
/writeoffs/<id>факт списания

Примем решения, какие методы будем использовать с каждым из ресурсов:

/booksкнигиGETPOST
/books/<id>книгаGETPUT
/shelvesместа храненияGETPOST
/shelves/<id>место храненияGETPUTDELETE
/placesфакты храненияGETPOST
/places/<id>факт храненияGETPUTDELETE
/usersчитателиGETPOST
/users/<id>читательGETPUT
/readsфакты чтенияGETPOST
/reads/<id>факт чтенияGETPUT
/writeoffsфакты списанияGETPOST
/writeoffs/<id>факт списанияGETPUT

Удаление книг, читателей, фактов чтения и списания книг не предусматриваем.

А теперь попробуем увязать логические операции, которые требуется реализовать, с получившимися запросами:

создать учетную запись книги
/books POST
изменить учетную запись книги
/books/<id> PUT
поставить на полку
/places POST
снять с полки
/places/<id> DELETE
переставить с полки на полку
/places/<id> PUT
начать читать книгу
/places/<id> DELETE
/reads POST
закончить читать книгу
/reads/<id> PUT
/places POST
списать книгу
/writeoffs POST
/places/<id> DELETE
/books/<id> PUT

Что не так с отображением логических операций на запросы к ресурсам?

Проблема в том, что некоторые логические операции отобразились на последовательности из нескольких RESTful запросов. Но наши операции с книгами должны выполняться как одно целое (!) в рамках одной транзакции. Следовательно, реализация операций "начать читать книгу", "закончить читать книгу" и "списать книгу" как несколько последовательных запросов к серверу не может нас устроить. Каждая операция должна быть отображена на один запрос, обработчик которого реализует всю необходимую логику!

Вычеркиваем лишнее:

начать читать книгу
/places/<id> DELETE
/reads POST
закончить читать книгу
/reads/<id> PUT
/places POST
списать книгу
/writeoffs POST
/places/<id> DELETE
/books/<id> PUT

Вычеркнутую функциональность придется возложить на оставшиеся запросы. То есть, запрос /reads POST не только создаст факт чтения книги, но и удалит факт хранения книги на полке. Точно так же обработчики запросов /reads/<id> PUT и /writeoffs POST возьмут на себя дополнительную работу.

Таким образом, на REST запросы нужно смотреть как на нетривиальные операции, имеющие сложное логическое содержание. Запрос на создание (POST), изменение (PUT) или удаление (DELETE) одного ресурса может привести к изменению (созданию, удалению) нескольких различных сущностей, каждая из которых отображается на различные ресурсы. Осознав это, обретаем необходимую степень свободы в проектировании RESTful приложений.

Продолжение следует. В ближайшее время я планирую рассказать о взаимоотношениях модели REST и протокола HTTP, а также рассмотреть создание простого RESTful приложения на микрофреймворке Flask.

Комментариев нет:

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