Введение
Данное руководство раскрывает лучшие практики разработки http+json api.
Основы
Обязательное использование TLS
Требуйте подключение с использованием TLS, без исключений. Не вижу смысла даже объяснять, почему полезно TLS и чем опасен отказ от его использования. Просто возьмите за привычку использовать TLS всегда, когда это возможно.
В идеале, просто отклоняйте любые не TLS запросы, не отвечая на запросы на порт 80, что бы исключить любой небезопасный обмен данными. В случае, когда это не возможно, просто отвечайте: 403 Forbidden.
Переадресация крайне нежелательна, так как оставляет возможность для неправильного поведения клиентов. Клиенты, полагающиеся на переадресацию, удваивают трафик и компромитируют TLS, так как данные уже были отправлены в открытом виде первым запросом.
Версия в заголовке Accept
Версия api должна объявляться с самого начала. Указывайте версию вместе с типом содержимого и прочим:
Accept: application/vnd.heroku+json; version=3
Желательно не использовать понятие «версия по-умолчанию». Вместо этого клиенты должны явно указывать версию в запросе.
Поддержка кэширования с использованием Etags
Включайте заголовок Etag во все запросы для идентификации версии возвращаемых ресурсов. У пользователя должна быть возможность проверить актуальность данных, указывая значение If-None-Match в запросе.
Отслеживание запросов с помощью Request-Ids
Включайте Request-Id со значением UUID в каждый ответ API. Наличие идентичных значений в логах сервера и клиента значительно упрощает отладку при работе с вашим API.
Пагинация
Используйте пагинацию во всех ответах, связанных с большими объемами данных. Для указания размера ответа можно использовать Content-Range в запросе.
Запросы (Request)
Возврат кодов, соответствующих результату операции
Возвращайте соответствующие http коды статуса в каждом ответе. Успешные ответы должны содержать следующие коды:
- 200 — для GET запроса и для синхронных DETELE и PATCH
- 201 — для синхронного POST запроса
- 202 — для асинхронных POST, DELETE и PATCH запросов
- 206 — для успешного частичного ответа на GET запрос
Уделите внимание ошибкам аутентификации и прав доступа:
- 401 Unautorized — пользователь не авторизован
- 403 Forbidden — доступ запрещен из-за недостатка прав
Дополнительные коды, указывающие на ошибки:
- 422 Unprocessable Entity — запрос корректный, но содержит неверные параметры
- 429 Too Many Requests — превышено лимит частоты подключений, попробуйте позже
- 500 Internal Server Error — Внутренняя ошибка сервера, можно обращаться к администратору
Для более подробной информации обратитесь к руководству по http.
Предоставляйте полные данные, где это возможно
Полные данные представления (объект со всеми атрибутами) должны отправляться в ответе, когда это возможно. Всегда предоставляйте полные данные при ответах 200 и 201, включая PUT/PATH и DELETE запросы, например:
$ curl -X DELETE \ https://myservice.ru/apps/1dbc/domains/0dbc HTTP/1.1 200 OK Content-Type: application/json;charset=utf-8 ... { "created_at": "2014-01-01T13:00:00Z", "hostname": "subdomain.example.com", "id": "32167543-8cb9-014f-8654-965874aabcdf", "updated_at": "2014-01-01T14:00:00Z" }
Ответ 202 может не включать полные данные, например:
$ curl -X DELETE \ https://myservice.ru/apps/1dcb/dynos/05dd HTTP/1.1 202 Accepted Content-Type: application/json;charset=utf-8 ... {}
Сериализованный JSON в теле запроса
Принимайте сериализованные JSON данные в дополнение или в качестве нормализованных данных формы. Например:
$ curl -X POST https://myservice.ru/apps \ -H "Content-Type: application/json" \ -d '{"name": "myapp"}' { "id": "32167543-8cb9-014f-8654-965874aabcdf", "name": "myapp", "owner": { "email": "ownermail@examplemail.ru", "id": "32167543-8cb9-014f-8654-965874aabcdf" }, ... }
Используйте последовательный формат для путей
Именование ресурсов
Используйте множество имен ресурсов, лишь бы это не угрожало уникальности каждого из них в системе и сохраняло ее целостность.
Действия
Предпочтительно использовать конструкции, которые не требуют специальных действий для определенных ресурсом. В случае, когда действие необходимо, указывайте его стандартным префиксом actions —/resources/:resource/actions/:action ,например:
/runs/{run_id}/actions/stop
Все в нижнем регистре
Используйте пути и имена только в нижнем регистре и только тире в качестве разделителя слов:
myservice-api.ru/users myservice-api.ru/app-setups
Для атрибутов также желательно использование нижнего регистра, но необходимо использование символа подчеркивания в качестве разделителя, для совместимости с JavaScript. Например:
service_class: "first"
Разыменование для удобства доступа
В некоторых случаях, для пользователя не удобно использование ID ресурса для его идентификации. Например пользователь может знать имя ресурса, а API идентифицирует ресурсы только по UUID. В подобных случаях лучше использовать для идентификации несколько параметров: уникальный идентификатор, имя и др.:
$ curl https://myservice.ru/apps/{app_id_or_name} $ curl https://myservice.ru/apps/54bafcf0-c322 $ curl https://myservice.ru/apps/www-prod
Конечно же, не допустимо использование одно имени взамен id.
Минимизация вложенности пути
В случаях организации данных, когда используются вложенные уровни предок/потомок, могут складываться пути с большой глубиной вложенности:
/orgs/{org_id}/apps/{app_id}/dynos/{dyno_id}
Ограничивайте уровень вложенности расположением ресурсов в корне. И используйте вложенность только для указания принадлежности и принадлежности данных. Для примера, dyno относится к app, который относится к org:
/orgs/{org_id} /orgs/{org_id}/apps /apps/{app_id} /apps/{app_id}/dynos /dynos/{dyno_id}
Ответы (Response)
Включайте (UU)ID ресурса в ответ
"id": "32167543-8cb9-014f-8654-965874aabcdf"
Используйте стандартные метки времени
Применяйте created_at и updated_at для меток времени создания и последнего обновления ресурса по-умолчанию:
{ ... "created_at": "2014-01-01T13:00:00Z", "updated_at": "2014-01-01T14:00:00Z", ... }
Эти метки времени могут быть не нужны для определенных типов ресурсов и могут не использоваться.
Форматируйте время по стандарту ISO8601
Прием и отправка даты/времени должны осуществляться только в UTC. Формат должен соответствовать ISO8601:
"finished_at": "2014-01-01T15:00:00Z"
Вложенные идентификаторы связанных данных
Упаковывайте значение ключа для связанного объекта вложением:
{ "name": "service-production", "owner": { "id": "5d8596b0..." }, ... }
Вместо:
{ "name": "service-production", "owner_id": "5d8596b0...", ... }
Это дает возможность указания большего количества информации о связанном объекте без изменения структуры ответа или усложнения полей ответа:
{ "name": "service-production", "owner": { "id": "5d8596b0...", "name": "Vasya", "email": "vasya@vasyamail.ru" }, ... }
Структурируйте информацию об ошибках
Создавайте последовательные и структурированные ответы при возникновении ошибок. Включайте идентификатор id типа ошибки, краткое описание message и url, указывающий на подробную информацию по данной ошибке:
HTTP/1.1 429 Too Many Requests
{ "id": "rate_limit", "message": "Account reached its API rate limit.", "url": "https://docs.service.com/rate-limits" }
Документируйте формат сообщений об ошибках и все возможные типы ошибок, которые может получить клиент.
Отображайте сведения о приближении лимита частоты запросов
Ограничение максимальной частоты запросов клиента — отличный способ поддержания уровня качества обслуживания всех клиентов сервиса. Вы можете использовать алгоритм маркерной корзины (Token bucket) для определения частоты запросов.
Возвращайте оставшийся лимит запросов в каждом ответе используя RateLimit-Remaining.
Всегда используйте минифицированный JSON
Лишние пробелы только раздувают запросы и ответы. В то время, как большинство вменяемых клиентов автоматически отображают JSON в удобочитаемом формате. Так что, лучше всего работать с минифицированным JSON:
{"beta":false,"email":"vasya@vasyamail.ru","id":"32167543-8cb9-014f-8654-965874aabcdf","last_login":"2014-01-01T14:00:00Z", "created_at":"2012-01-01T13:00:00Z","updated_at":"2012-01-01T14:00:00Z"}
Вместо:
{ "beta": false, "email": "vasya@vasyamail.ru", "id": "32167543-8cb9-014f-8654-965874aabcdf", "last_login": "2014-01-01T14:00:00Z", "created_at": "2014-01-01T13:00:00Z", "updated_at": "2014-01-01T14:00:00Z" }
Вы можете опционально предоставлять JSON в удобочитаемом формате, по специальному запросу через параметр (например ?minify=false) или через параметр Accept (например Accept: application/vnd.myvendor+json; version=7; indent=4;).
Особенности
Предоставление машино-читаемой схемы JSON
Предоставляйте машино-читаемую схему для описания API. Используйте prmd для работы со схемой. Убедитесь в ее корректности: prmd verify.
Предоставляйте человеко-читаемую документацию
Не забывайте документировать все возможности и особенности API на уровне достаточном для понимания его другими разработчиками.
При использовании prmd для создания схемы, шаблон документации можно легко получить командой prmd doc.
Дополнительно полезно предоставлять следующую информацию:
- Аутентификация, включая получение и использование токенов.
- Версии API и их стабильность, включая информацию о том, как выбрать нужную версию.
- Общее описание заголовков запросов и ответов.
- Описание формата ответов при ошибке.
- Примеры использования API на разных языках.
Предоставление рабочих примеров
Создайте набор рабочих примеров, на которых пользователь сможет вживую увидеть работу API.
При использовании prmd для генерирования документации, так же, будут сгенерированы примеры использования для каждой ветви схемы.
Указывайте стабильность
Стабильность API и всех есть компонентов должна быть описана.
Если указан stable или production-ready статус, не применяйте изменений, не совместимых с данной версией, просто создайте новую версию API.