Статьи

Общий шаблон дизайна REST

Основываясь на том же архитектурном паттерне в сети, в наши дни «REST» все больше доминирует в реализации SOA (Service Oriented Architecture). В этой статье мы обсудим некоторые основные принципы проектирования REST.

SOAP: модель удаленного вызова процедур

До того, как REST станет доминирующим, большая часть архитектуры SOA построена вокруг стека WS *, который по сути является моделью RPC (удаленного вызова процедур).

В соответствии с этой моделью «Сервис» структурирован как некая «Процедура», предоставляемая системой. Например, WSDL используется для определения синтаксиса вызова процедуры (например, имени процедуры, параметра и их структуры). SOAP используется для определения способа кодирования вызова процедуры в строку XML. Существуют и другие стандарты WS *, определяющие протоколы более высокого уровня, например, как передавать учетные данные безопасности, как выполнять вызов транзакционной процедуры, как определить местоположение службы … и т. Д.

К сожалению, стек WS * становится настолько сложным, что для его использования требуется крутой курс обучения. С другой стороны, он не достигает своей первоначальной цели взаимодействия (вероятно, имеет дело с различной интерпретацией того, что говорится в спецификации). За последние 2 года развитие технологии WS * замедлилось, и импульс был перенесен на другую модель; ОСТАЛЬНЫЕ.

ОТДЫХ: Модель, ориентированная на ресурсы

REST (REpresentation State Transfer) введен Роем ​​Филдингом, когда он запечатлел базовый архитектурный паттерн, который делает сеть настолько успешной. Наблюдая за тем, как организованы веб-страницы и как они связаны друг с другом, REST моделируется большим количеством «ресурсов», которые «связывают» друг с другом. Как существенное отличие от WS *, REST повышает важность «ресурсов», а также их «связи», с другой стороны, он понижает важность «процедур».

В модели WS * «Сервис» в SOA организован как большое количество «Ресурсов». Каждый ресурс будет иметь URI, который делает его глобально идентифицируемым. Ресурс представлен некоторым форматом «Представление», который обычно извлекается идемпотентным HTTP GET. Представление может включать другой URI, который ссылается на другие ресурсы. Это эмулирует HTML-ссылку между веб-страницами и предоставляет клиенту мощный способ обнаружить другие сервисы, просматривая его ссылки. Это также делает возможным создание поисковой системы SOA.

С другой стороны, REST проигрывает аспект «Процедура» и определяет небольшое количество «действий» на основе существующих методов HTTP. Как мы уже говорили выше, HTTP GET используется для получения представления ресурса. Чтобы изменить ресурс, REST использует HTTP PUT с новым представлением, встроенным в тело HTTP. Чтобы удалить ресурс, используйте REST HTTP DELETE. Чтобы получить метаданные ресурса, REST использует HTTP HEAD. Обратите внимание, что во всех этих случаях тело HTTP не несет никакой информации о «процедуре». Это сильно отличается от WS * SOAP, где запрос всегда выполняется с использованием HTTP POST.

На первый взгляд кажется, что REST довольно ограничен с точки зрения количества процедур, которые он может поддерживать. Оказывается, это не так, REST позволяет любой процедуре (которая имеет побочный эффект) использовать HTTP POST. По сути, REST классифицирует операции по своей природе и связывает четко определенную семантику с этими категориями (то есть: GET для только чтения, PUT для обновления, DELETE для удаления, все вышеупомянутые являются идемпотентными), в то же время обеспечивая механизм расширения для операций, специфичных для приложения. (т.е.: POST для процедур подачи заявок, которые могут быть неидемпотентными).

Соглашение об именовании URI

Поскольку ресурс обычно сопоставляется с некоторым состоянием в системе, анализ его жизненного цикла является важным этапом при разработке способа создания ресурса и структуры URI.

Как правило, существуют вечные синглтонские « фабричные ресурсы », которые создают другие ресурсы. Фабричный ресурс обычно представляет «тип» ресурсов. Ресурс фабрики обычно имеет статический, общеизвестный URI, к которому добавляется множественная форма типа ресурса. Некоторые примеры …

http://xyz.com/books

http://xyz.com/users

« Экземпляр ресурса », который создается «Ресурсом фабрики», обычно представляет экземпляр этого типа ресурса. «Ресурсные экземпляры» обычно имеют ограниченный срок службы. Их URI обычно содержит некоторый уникальный идентификатор, чтобы можно было найти соответствующий экземпляр ресурса. Некоторые примеры …

http://xyz.com/books/4545
http://xyz.com/users/123

« Зависимый ресурс » обычно создается и принадлежит существующему ресурсу в течение части его жизненного цикла. Следовательно, «зависимый ресурс» имеет неявную зависимость жизненного цикла от своего родительского ресурса. При удалении родительского ресурса все принадлежащие ему зависимые ресурсы будут удалены автоматически. Зависимый ресурс использует URI, который имеет префикс своего родительского URI ресурса. Некоторые примеры …

http://xyz.com/books/4545/tableofcontent
http://xyz.com/users/123/shopping_cart

Создание ресурса

Чтобы создать экземпляр ресурса определенного типа, сделайте HTTP-запрос POST к URI фабричного ресурса. Если создание успешно, ответ будет содержать URI ресурса, который был создан.

Чтобы создать книгу …

POST /books HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<book>
<title>...</title>
<author>Ricky Ho</author>
</book>
HTTP/1.1 201 Created
Content-Type: application/xml; charset=utf-8
Location: /books/4545

<ref>http://xyz.com/books/4545</ref>

Чтобы создать зависимый ресурс, создайте HTTP-запрос POST к URI его ресурса.

Чтобы загрузить содержание книги (используя HTTP POST) …

POST  /books/4545  HTTP/1.1
Host: example.org
Content-Type: application/pdf
Content-Length: nnnn

{pdf data}
HTTP/1.1 201 Created
Content-Type: application/pdf
Location: /books/4545/content

<ref>http://xyz.com/books/4545/tableofcontent</ref>

HTTP POST обычно используется для создания ресурса, когда его URI неизвестен клиенту до его создания. Однако, если URI известен клиенту, то следует использовать идемпотентный HTTP PUT с URI создаваемого ресурса.

Чтобы загрузить содержание книги (используя HTTP PUT) …

HTTP/1.1 201 Created
Content-Type: application/pdf
Location: /books/4545/content

<ref>http://xyz.com/books/4545/tableofcontent</ref>
HTTP/1.1 200 OK

Поиск ресурсов

Сделайте HTTP GET для URI ресурса фабрики, критерии передаются как параметры. (Обратите внимание, что это зависит от ресурса фабрики, чтобы интерпретировать параметр запроса).

Для поиска книг с определенным автором …

GET /books?author=Ricky HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<books>
<book>
<ref>http://xyz.com/books/4545</ref>
<title>...</title>
<author>Ricky</author>
</book>
<book>
<ref>http://xyz.com/books/4546</ref>
<title>...</title>
<author>Ricky</author>
</book>
</books>

 

Еще одна школа мысли — встраивать критерии в путь URI, например … http://xyz.com/books/author/Ricky

Я лично предпочитаю механизм параметров запроса, потому что он не предполагает какого-либо порядка критериев поиска.

Поиск конкретного ресурса

Сделать HTTP GET для URI объекта ресурса

Найти конкретную книгу …

GET /books/4545 HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<book>
<title>...</title>
<author>Ricky Ho</author>
</book>

В случае, если ресурс имеет несколько форматов представления. Клиент должен указать в HTTP-заголовке «Принять» своего запроса, какой формат он ожидает.

Поиск зависимого ресурса

Сделайте HTTP GET для URI зависимого объекта ресурса

Скачать оглавление определенной книги …

GET /books/4545/tableofcontent HTTP/1.1
Host: xyz.com
Content-Type: application/pdf
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: nnn

{pdf data}

Изменить ресурс

Сделайте HTTP PUT для URI объекта ресурса, передайте новое представление объекта в теле HTTP

Изменить название книги …

PUT /books/4545 HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<book>
<title>Changed title</title>
<author>Ricky Ho</author>
</book>
HTTP/1.1 200 OK

Удалить ресурс

Сделать HTTP-УДАЛЕНИЕ к URI объекта ресурса

Удалить книгу …

DELETE /books/4545 HTTP/1.1
Host: xyz.com
HTTP/1.1 200 OK

Ссылка на ресурс

В некоторых случаях мы не хотим создавать новый ресурс, но мы хотим добавить «ссылку» на существующий ресурс. Например, рассмотрим книгу, добавленную в корзину, которая является другим ресурсом.

Добавить книгу в корзину …

POST  /users/123/shopping_cart  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0" ?>
<add>
<ref>http://xyz.com/books/4545</ref>
</add>
HTTP/1.1 200 OK

Показать все элементы корзины …

GET  /users/123/shopping_cart  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0" ?>
<shopping_cart>
<ref>http://xyz.com/books/4545</ref>
...
<shopping_cart>

Обратите внимание, что ресурс корзины покупок содержит «ссылку на ресурс», которая действует как ссылки на другие ресурсы (то есть книги). Такие связи создают сеть ресурсов, так что клиент может обнаруживать различные ресурсы и перемещаться по ним.

Удалить книгу из корзины …

POST  /users/123/shopping_cart  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0" ?>
<remove>
<ref>http://xyz.com/books/4545</ref>
</remove>
HTTP/1.1 200 OK

Обратите внимание, что мы используем HTTP POST вместо HTTP DELETE для удаления ссылки на ресурс. Это потому, что мы удалили ссылку, но не сам ресурс. В этом случае книга все еще существует после того, как она вынута из корзины покупок.

Оформить заказ в корзину …

POST  /orders  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0" ?>
<ref>http://xyz.com/users/123/shopping_cart</ref>
HTTP/1.1 201 Created
Content-Type: application/xml; charset=utf-8
Location: /orders/2008/04/10/1001

<?xml version="1.0" ?>
<ref>http://xyz.com/orders/2008/04/10/1001</ref>

Обратите внимание, что здесь оформление заказа осуществляется путем создания другого ресурса «Заказ», который используется для отслеживания выполнения покупки.

Ресурс транзакции

Одна из распространенных критических замечаний в отношении REST заключается в том, что он так привязан к HTTP (который не поддерживает механизм обратного вызова клиента), поэтому выполнять асинхронную службу или уведомление о REST сложно. Итак, как мы реализуем долгосрочные транзакции (которые обычно требуют асинхронности и поддержки обратного вызова) в REST?

Основная идея состоит в том, чтобы немедленно создать «Ресурс транзакции» для возврата клиенту. Хотя фактическая обработка происходит асинхронно в фоновом режиме, клиент в любой момент может запросить «Ресурс транзакции» для получения последнего статуса обработки. Давайте рассмотрим пример запроса на печать книги, который может занять много времени.

Распечатать книгу

POST  /books/123  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

?xml version="1.0" ?>
<print>http://xyz.com/printers/abc</print>
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Location: /transactions/1234

<?xml version="1.0" ?>
<ref>http://xyz.com/transactions/1234</ref>

Обратите внимание, что немедленно создается ответ, содержащий URI ресурса транзакции, даже до запуска задания на печать. Клиент может опрашивать ресурс транзакции, чтобы получить последний статус задания на печать.

Проверьте статус задания на печать …

GET /transactions/1234 HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<transaction>
<type>PrintJob</type>
<status>In Progress</status>
</transaction>

Также возможно отменить транзакцию, если она еще не завершена.

Отменить задание на печать

POST  /transactions/1234  HTTP/1.1
Host: xyz.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0" ?>
<cancel/>
HTTP/1.1 200 OK

Вывод

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