Слава REST: действительно ли она настолько славна?
28 января 2023 г.С тех пор как я начал работать в Apache APISIX, я пытался углубить свое понимание REST с помощью различных средств. Вы читали мой обзор книги по шаблонам проектирования API?
В современной литературе REST обычно рекламируется как лучшая вещь после нарезанного хлеба. Тем не менее, это сопряжено с множеством проблем. В 2010(!) Мартин Фаулер написал статью о славе REST. Он перечисляет три шага, чтобы API стал настоящим REST:
На каждом из этих шагов скрываются проблемы. В этом сообщении блога основное внимание уделяется перечислению некоторых из них и предоставлению советов по их решению.
Ресурсы
REST появился из минусов SOAP. SOAP предоставляет единую конечную точку и выполняет код в зависимости от полезной нагрузки. Идея REST заключается в предоставлении нескольких конечных точек, каждая из которых выполняет свой код.
Я буду честен; на этом этапе есть несколько вопросов. Самый большой из них связан с угадыванием одной личности из существующей. Если идентификаторы ресурсов являются последовательными или даже числовыми, легко угадать конечные точки других ресурсов, например,, от /customers/1
до /customers/2.
Решение заключается в использовании непоследовательных нечисловых идентификаторов, то есть, универсальных уникальных идентификаторов.
Давайте рассмотрим модель зрелости REST.
Глаголы HTTP
Глаголы HTTP — это следующий шаг к славе REST. Они возникают в результате взаимодействия с HTML «в прошлом». Взаимодействия происходили из операций Create Read Update Delete (CRUD).
Это довольно просто:
| Операция | Глагол |
|----|----|
| Создать | ОТПРАВИТЬ
|
| Читать | ПОЛУЧИТЬ
|
| Обновить | ПОСТАВИТЬ
|
| | ИСПРАВЛЕНИЕ
|
| Удалить | УДАЛИТЬ |
Основная проблема с API заключается в том, что вам нужно выйти за рамки CRUD. Давайте представим конкретный пример с банковским переводом: он берет деньги с одного счета и переводит их на другой. Как мы будем его моделировать?
Мы могли бы использовать исходную учетную запись в качестве ресурса, например,, /accounts/a1b2c3d4e5f6
. Целевая учетная запись, сумма и т. д. могут быть переданы в качестве параметров запроса или в теле. Но какой глагол HTTP мы будем использовать?
Он действительно изменяет идентифицированный ресурс, но имеет «побочные эффекты»: он также изменяет другой ресурс, целевую учетную запись. Вот несколько вариантов того, как управлять командой HTTP:
* Используйте POST
, потому что он изменяет исходный ресурс. Это вводит в заблуждение, потому что ничего не говорит о побочных эффектах.
* Используйте специальную команду HTTP, например,, TRANSFER
. Это не говорит само за себя и противоречит принципам REST.
* Используйте POST
с так называемым пользовательским методом. Пользовательские методы являются предложением Google по улучшению API:
Пользовательские методы следует использовать только для функций, которые нельзя легко выразить с помощью стандартных методов; по возможности отдавайте предпочтение стандартным методам из-за их последовательной семантики.
HTTP URI должен использовать символ :
, за которым следует пользовательская команда.
Вот наш URI банковского перевода: /accounts/a1b2c3d4e5f6:transfer
.
Какая лучшая альтернатива? "Это зависит".
Гипермедиа
Фаулер описывает элементы управления Hypermedia как последний шаг к славе REST. В настоящее время он известен как Hypermedia As The Engine of Application State (HATEOAS):
<цитата>При использовании HATEOAS клиент взаимодействует с сетевым приложением, чьи серверы приложений динамически предоставляют информацию через гипермедиа. Клиенту REST практически не нужны предварительные знания о том, как взаимодействовать с приложением или сервером, помимо общего понимания гипермедиа.
HATEOAS — это концепция; вот возможная реализация, взятая из Википедии. Когда кто-то запрашивает банковский счет, скажем, /accounts/a1b2c3d4e5f6
, ответ содержит ссылки на действия, возможные с этим конкретным банковским счетом:
{
"account": {
"account_number": "a1b2c3d4e5f6",
"balance": {
"currency": "USD",
"value": 100.00
},
"links": {
"_self": "/accounts/a1b2c3d4e5f6",
"deposit": "/accounts/a1b2c3d4e5f6:deposit",
"withdrawal": "/accounts/a1b2c3d4e5f6:withdrawal",
"transfer": "/accounts/a1b2c3d4e5f6:transfer",
"close-request": "/accounts/a1b2c3d4e5f6:close-request"
}
}
}
Если баланс отрицательный, будет доступна только ссылка для депозита:
{
"account": {
"account_number": "a1b2c3d4e5f6",
"balance": {
"currency": "USD",
"value": 100.00
},
"links": {
"_self": "/accounts/a1b2c3d4e5f6",
"deposit": "/accounts/a1b2c3d4e5f6:deposit",
}
}
}
Распространенной проблемой REST является отсутствие стандартов; HATEOAS ничем не отличается. Первой попыткой внести некоторую степень стандартизации был язык гипертекстовых приложений JSON, иначе HAL. Обратите внимание, что он был запущен в 2012 году; последняя версия датирована 2016 годом и все еще находится в стадии разработки.
Вот краткая диаграмма, обобщающая предложение:
Мы можем переделать вышеизложенное с помощью HAL следующим образом:
GET /accounts/a1b2c3d4e5f6 HTTP/1.1
Accept: application/hal+json
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
"account": {
"account_number": "a1b2c3d4e5f6",
"balance": {
"currency": "USD",
"value": 100.00
},
"_links": { <1>
"self": { <2>
"href" : "/accounts/a1b2c3d4e5f6",
"methods": ["GET"] <3>
},
"deposit": {
"href" : "/accounts/a1b2c3d4e5f6:deposit", <4>
"methods": ["POST"] <3>
}
}
}
}
- Доступные ссылки
- Ссылка на себя
- Укажите, какой глагол HTTP можно использовать
- Ссылка на депозит
Другой попыткой стандартизации является RFC 8288, он же Web Linking. Он описывает формат и содержит реестр отношений ссылок, например,, альтернативный
и авторское право
. Наиболее существенное отличие HAL заключается в том, что RFC 8288 передает ссылки через заголовки ответа HTTP.
HTTP/2 200 OK
Link: </accounts/a1b2c3d4e5f6> rel="self";
method="GET", <1>
</accounts/a1b2c3d4e5f6:deposit> rel="https://my.bank/deposit";
title="Deposit";
method="POST" <2>
{
"account": {
"account_number": "a1b2c3d4e5f6",
"balance": {
"currency": "USD",
"value": 100.00
}
}
}
- Ссылка на текущий ресурс с нестандартным типом отношения
self
- Ссылка на депозит с расширением
https://my.bank/deposit
и произвольным целевым атрибутомtitle
Доступны другие спецификации альтернативных типов носителей.
| Имя | Описание | Предоставлено | |----|----|----| | Единая основа для обмена представлениями | Формат документа UBER — это минимальный тип гипермедиа для чтения и записи, предназначенный для поддержки простой передачи состояний и специальных переходов на основе гипермедиа. В этой спецификации описаны варианты формата XML и JSON, а также приведены рекомендации по поддержке сообщений в кодировке UBER по протоколу HTTP. | Физические лица | | Collection+JSON | Collection+JSON — это тип гипермедиа для чтения и записи на основе JSON, предназначенный для поддержки управления простыми коллекциями и запросов к ним | Индивидуальный | | JSON: API | JSON: API — это спецификация того, как клиент должен запрашивать получение или изменение ресурсов, и как сервер должен отвечать на эти запросы. JSON: API можно легко расширить с помощью расширений и профилей. | Физические лица | | Сирена | Siren — это спецификация гипермедиа для представления сущностей. Поскольку HTML используется для визуального представления документов на веб-сайте, Siren — это спецификация для представления объектов через веб-API. Siren предлагает структуры для передачи информации о сущностях, действия для выполнения переходов между состояниями и ссылки для клиентской навигации. | Индивидуальный | | Семантика профиля уровня приложения | Документ ALPS можно использовать в качестве профиля для объяснения семантики приложения документа с типом носителя, не зависящим от приложения (например, HTML, HAL, Collection+JSON, Siren и т. д.). Это повышает возможность повторного использования документов профилей для разных типов носителей. | IETF |
Бонус: статус ответа HTTP
В сообщении Фаулера не упоминается статус ответа HTTP. Большинство читателей знакомы с диапазонами статусов:
* Информационные ответы: 100 – 199 * Успешные ответы: 200 – 299 * Сообщения перенаправления: 300 – 399 * Ответы клиента на ошибку: 400 – 499 * Ответы сервера на ошибки: 500 - 599
Аналогичным образом, большинство из них также имеют регулярно находимый HTTP-статус:
* 200 ОК, * 301 перемещен навсегда, * 302 найдено, * 401 Несанкционировано, * 403 Запрещено, * 404 Not Found и * Внутренняя ошибка сервера 500.
Проблема в том, что помимо этих простых случаев, это беспорядок. Например, посмотрите на этот вопрос StackOverflow a>: «Какой код состояния HTTP означает «Еще не готов, попробуйте еще раз позже?» Вот краткое изложение предложенных ответов, от самых популярных до самых низких:
* 503 Сервис недоступен * 202 Принято (принятый ответ) * 423 Заблокировано * 404 Не Найдено * 302 Найдено * 409 Конфликт * 501 Не реализовано (проголосовали против)
Это не прямой ответ; вокруг альтернатив было много споров. Для протокола, я думаю, что принятый ответ является правильным.
Это уже много на стороне дизайнера, но на стороне клиента тоже много неопределенности, поскольку некоторые крупные поставщики API используют свои собственные коды состояния HTTP.
Заключение
"Слава REST" мало что значит. Не существует однозначной семантики, на которую можно положиться, несмотря на любое противоположное утверждение. Как бы то ни было, это зависит в основном от реализации и интерпретации: и то, и другое требует документирования пользовательского поведения, а не использования общей спецификации.
Самым большим недостатком SOAP была его сложность и ориентация на крупные компании, но, по крайней мере, он обеспечивал общий набор стандартных спецификаций. Промышленность заменила его REST, не спецификацией, а архитектурным сайтом.
REST проще и, следовательно, более доступен, но требует больших усилий, которые меняются от проекта к проекту.
Есть инициативы по обеспечению некоторой стандартизации, но их немного, и некоторые расходятся с другими. Более того, у них низкая тяга, поэтому люди их не узнают, что создает замкнутый круг. Я вряд ли рекомендую вернуться к SOAP, хотя иногда мне этого не хватает.
Первоначально опубликовано на сайте A Java Geek 22 января 2023 г. р>
Оригинал