Разработайте хороший REST API

API

Компания Apigee в Силиконовой долине дала руководство по проектированию REST API, можно сказать, что достижения компании в разработке и управлении API очевидны для всех. Руководящие принципы, которые он предоставляет, можно сказать, сочетают в себе собственный фактический опыт разработки, фактический опыт эксплуатации многих крупных платформ и стандартные спецификации http. Очень стоит прочитать.

Во-первых, вам нужно иметь базовое концептуальное представление о REST API, а затем читать дальше:

1. Моделирование данных на основе бизнес-домена, а не функционального моделирования.

Например, получить всех собак

GET /api/dogs 

получить конкретную собаку

GET /api/dogs/{id}

получить собак с определенным именем

GET /api/dogs/?name=xxx

создать собаку

POST /api/dogs

поменять собаку

PUT /api/dogs/{id}

удалить собаку

DELETE /api/dogs/{id}

Стандартные методы HTTP уже предоставляют наборОбычная операционная семантика.

API на основе функционального моделирования обычно выглядит так:

/getAllDogs
/getDogsByNames
/getAllBabyDogs
/createDogs
/createThreeDogs
/saveDogs

Вышеупомянутое я часто вижу в коде новичка. Код, который делает это, да, работает, но не является «стандартным», почему? API, основанные на функциональном моделировании, сначала приведут к увеличению кривой обучения, что не так просто начать, и часто это означает, что необходимо запомнить большое количество URL-адресов (swagger решит часть этой проблемы, но не все). .

Использование стандартных методов HTTP в качестве базовой семантики для манипулирования данными лучше, чем его стандартное повсеместное распространение. На данный момент большие платформы Github, Heroku и т. д. справляются лучше всего.

2. Представление проектных данных

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

И используйте ссылку, чтобы выразить связь между ресурсами:

Например:

{ 
 "id": "12345678",
 "kind": "Dog"
 "name": "Lassie",
 "furColor": "brown",
 "ownerID": "98765432"
}

Лучший способ:

{
 "id": "12345678",
 "kind": "Dog"
 "name": "Lassie",
 "furColor": "brown",
 "ownerID": "98765432",
 "ownerLink": "https://dogtracker.com/persons/98765432"
}

Более краткое представление:

{
 "id": "12345678",
 "kind": "Dog"
 "name": "Lassie",
 "furColor": "brown",
 "owner": "https://dogtracker.com/persons/98765432"
}

Преимущество этого в том, что клиенту не нужно перестраивать URL для получения владельца, что более удобно в использовании. И Google Drive API, и Github используют этот подход. Но это не без вреда, проще всего подумать, а что, если URL-адрес изменится? Производство, подготовка и тестирование используют разные домены. Один из способов: используйте относительные пути вместо абсолютных. Но и это не решает всей проблемы.

3. Создайте представление URL

Два принципа: регулярный и предсказуемый.

  1. использовать только существительные
  2. иметь точку входа
  3. Выберите подходящую форму идентификатора
  4. Чтобы выразить связь между ресурсами
  5. Для поддержки запроса
  6. Для поддержки частичного возврата ресурсов
  7. Обработка более сложной вычислительной логики

использовать только существительные: Используйте существительные в URL-адресах, избегайте глаголов, когда вы используете глаголы, вы моделируете функциональность, а не данные.

иметь точку входа: В принципе, API должен иметь корневой путь «/», который возвращает карту URL-адресов, включая URL-адреса, соответствующие всем ресурсам. Это облегчает клиентам обнаружение и использование API.

Выберите подходящую форму идентификатора: Например, как выражается {id} в api/dogs/{id}, похоже ли оно на '/dogs/1' или '/dogs/haha'? Как правило, это зависит от серверной базы данных. В большинстве случаев , используйте RDBMS, функцию автоматического увеличения первичного ключа, такую ​​как mysql, я предпочитаю использовать целое число автоматического увеличения первичного ключа в качестве идентификатора объекта напрямую, чтобы избежать многих проблем.Если вы используете MongoDB, вы также можете попробовать использовать строка в качестве идентификатора объекта, который можно прочитать.Производительность улучшится, но как поддерживать глобально уникальный первичный ключ символа, вы должны подумать дважды.

Чтобы выразить связь между ресурсами: Например, GET /persons/1/dogs возвращает всех собак, принадлежащих человеку 1.
Этот шаблон может быть выражен как:

/{relationship-name}[/{resource-id}]/…/{relationship-name}[/{resource-id}]

Для поддержки запроса:

GET /persons;1/dogs GET /persons;name=blabla/dogs 

Этот шаблон может быть выражен как:

/{relationship-name}[;{selector}]/…/{relationship-name}[;{selector}]

Более сложные условия запроса:

GET /dogs?color=red&state=running&location=park 

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

Для поддержки частичного возврата ресурсов:

/dogs?fields=name,color,location 

Возвращаемый ресурс содержит только три типа информации: имя, цвет и местоположение.

Обработка более сложной вычислительной логики: Есть много примеров, таких как конвертация валюты, решение, данное большинством разработчиков:

/convert/100/EUR/CNY или /convert?quantity=100&unit=EUR&in=CNY

Помните: URL-адрес используется для описания ресурса ресурса, а не процесса вычисления. Используйте существительные в URL-адресах и избегайте глаголов.

Улучшенное решение 1:

GET /monetary-amount/100/EUR HTTP/1.1
Host: Currency-Converter.com
Accept-Currency:CNY //在http的header中添加Accept-Currency来指明货币的种类。

Улучшенная схема 2:

POST /currency-converter HTTP/1.1
Host: Currency-Converter.com 
Content-Length: 69
{
"amount": 100,
"inputCurrency": "EUR",
"outputCurrency": "CNY"
}

Оба решения выполнимы, но в решении 2 есть два нюанса: результаты, возвращаемые POST, могут не кэшироваться на стороне сервера, вы строите процесс вычисления, а не представление ресурсов, как понять? Так же, как и «процедура хранения» в операциях с базой данных, вы можете использовать ее, и она мощная, но интерфейс усложняется, а логика становится связанной.

4. Рефлексия. Разработайте представление данных.

  1. добавить ссылку на себя
  2. сбор данных
  3. Пейджинг данных
  4. Формат данных

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

{
 "user": {
   "html_url": "octocat (The Octocat)",
   "type": "User",
   "url": "https://api.github.com/users/octocat"
 }
}

Данные коллекции:

Вариант 1. Коллекция collection также является ресурсом, а также имеет атрибуты self и kind, чтобы все отдельные сущности и коллекции имели более унифицированную спецификацию.

{
	"self": "https://dogtracker.com/dogs",
	"kind": "Collection",
	"contents": [
	 	{
	 		"self": "https://dogtracker.com/dogs/12344",
	 		"kind": "Dog",
		 	"name": "Fido",
		 	"furColor": "white"
		 },
		 {
			 "self": "https://dogtracker.com/dogs/12345",
			 "kind": "Dog",
			 "name": "Rover",
			 "furColor": "brown"
		 }
	 ]
}

Вариант 2: выглядит более лаконично, но клиенту может потребоваться добавить дополнительную логику для обработки коллекции.

[
	{
		"self": "https://dogtracker.com/dogs/12344",
		"kind": "Dog",
	 	"name": "Fido",
	 	"furColor": "white"
	 },
	 {
		 "self": "https://dogtracker.com/dogs/12345",
		 "kind": "Dog",
		 "name": "Rover",
		 "furColor": "brown"
	 }
]

Другой подход заключается в использовании заголовка пользовательского типа мультимедиа (например, «Коллекция + JSON») для коллекции. Этот метод выполним, но усложнит логику обработки клиента.

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

GET https://dogtracker.com/dogs?limit=25,offset=0 
返回
{
"self": "https://dogtracker.com/dogs?limit=25,offset=0",
"kind": "Page",
"pageOf": "https://dogtracker.com/dogs",
"next": "https://dogtracker.com/dogs?limit=25,offset=25",
"contents": [...】
}

В возвращаемых данных добавьте «pageOf», чтобы указать начальную точку запроса, «next», чтобы указать URL-адрес следующей страницы, а при возврате на вторую страницу добавьте «previous», чтобы указать предыдущую страницу.

Формат данных:Большинство API теперь почти поддерживают только данные формата JOSN в качестве ввода и вывода.Если вы хотите поддерживать больше форматов данных, вам следует поддерживать Http Accept Header.

Могу ли я использовать HTML в качестве выходного формата? Да, но вы теряете гибкость остального API. Большинство современных веб-приложений используют дизайн REST API + SPA. Сторона SPA использует Angular и другие фреймворки для самостоятельного рендеринга HTML. REST API предоставляет только службы данных, а интерфейс и сервер взаимодействуют через данные JSON, таким образом реализовать полное разделение фронтэнда и бэкенда.

Если вы выбираете JSON в качестве единственного формата данных, то лучше поддерживать метод исправления Http.Существует два режима исправления: JSON Patch и JSON Merge Patch.Выберите один для операций обновления ресурсов. Также существует множество API, которые предоставляют только PUT для обновления ресурсов, а это означает, что каждый запрос должен отправлять весь ресурс, что неизбежно потребует больше полезной нагрузки, но его проще реализовать.

5. Обработка ошибок

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

6. Аутентификация и авторизация

Жизнь слишком коротка, чтобы использовать OAuth2. По крайней мере, следует использовать аутентификацию на основе токенов.

7. SDK

В дополнение к вашему REST API можно запустить SDK, как и в AWS, для каждого сервиса есть SDK для соответствующего языка программирования. Это упрощает использование вашего API сторонними разработчиками. Чаще встречается на платформах SaaS. Однако для небольших платформ необходимо учитывать затраты на техническое обслуживание.

8. Управление версиями

Проблема контроля версий REST API очень спорная тема.В интернете много предложений.Здесь мы не просто даем конкретный метод,а даем несколько возможных идей.Конкретную реализацию нужно определить самому:

  1. Не (явно) поддерживает несколько версий
  2. Использование заголовка Http Accept

Первый ничего не делает и не поддерживает несколько версий API. В основе этой идеи лежит то, что, согласно опросу, большинство сервисов малых и средних платформ, масштаб клиента находится в контролируемом диапазоне, и обновление API не будет очень частым, вам нужно только уведомить своих клиентов. , в определенный момент времени API будет обновлен, а затем на стороне сервера будет выполнена некоторая совместимая миграция данных, например добавление или удаление имени столбца в таблице базы данных. В большинстве случаев поддержка многоверсионных API неблагодарна, стоимость тестирования и развертывания велика, а пользы мало. Что вам нужно сделать, так это поддерживать совместимость с единственной доступной службой API, а не предоставлять пользователям несколько версий API.

Во-вторых, если вы должны поддерживать управление версиями, добавьте информацию о версии в заголовок принятия http, не используйте информацию о версии в URL-адресе и не используйте /api/v1/xxx.

Суммировать

docs-APIs.API gee.IO/files/Web-of...docs-APIs.API gee.IO

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

В этой статье даются принципиальные рекомендации. Для группы разработчиков я предлагаю сформулировать более конкретную спецификацию внутри команды. Подробности см. по адресу:Zalando RESTful API and Event Scheme GuidelinesСпецификации охватывают множество деталей, чем подробнее спецификации, тем унифицированнее стиль кода и выше эффективность коммуникации команды.