Понимание вопросов проектирования API Rest для инженеров-программистов

REST API лежат в основе подавляющего большинства современных программных приложений, служа стандартным архитектурным стилем для веб-сервисов. Для инженеров-программистов овладение REST API дизайном не является обязательным — это фундаментальный навык, который непосредственно влияет на надежность системы, масштабируемость и опыт разработчиков. Плохо спроектированный API создает трение для потребителей, приводит к кошмарам интеграции и несет большие затраты на обслуживание. И наоборот, хорошо продуманный API абстрагирует сложность, обеспечивает бесшовную связь между услугами и изящно развивается с течением времени.

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

Что такое REST API?

REST означает Representational State Transfer, архитектурный стиль, представленный Роем Филдингом в его докторской диссертации 2000 года. По своей сути, REST API представляет собой набор ограничений, которые регулируют то, как клиенты и серверы обмениваются данными по HTTP. В отличие от более ранних подходов к удаленному вызову процедур (RPC), REST фокусируется на ресурсах — любой значимой части информации — а не на действиях. Каждый ресурс идентифицируется URI, и взаимодействия выполняются с использованием стандартных методов HTTP (GET, POST, PUT, PATCH, DELETE).

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

Популярность REST обусловлена его простотой, производительностью и масштабируемостью. Он использует вездесущий протокол HTTP, использует знакомые методы и возвращает данные в легких форматах, таких как JSON. Для инженеров-программистов понимание REST глубоко позволяет разрабатывать API, которые являются интуитивно понятными, совместимыми и поддерживающими, независимо от того, создаете ли вы API с открытым доступом или внутренний микросервис.

Основные принципы REST API Design

Хотя не все API строго придерживаются всех ограничений (некоторые из них более прагматичны, чем пуристские), следующие принципы составляют основу хорошего дизайна REST API.

Безгражданство

Каждый клиентский запрос должен быть автономным. Сервер не должен хранить какой-либо клиентский контекст между запросами. Это означает, что токены аутентификации, параметры запроса и все необходимые данные должны быть предоставлены в самом запросе. Безгражданство имеет значительные последствия: оно упрощает балансировку нагрузки, поскольку любой сервер может обрабатывать любой запрос, повышает надежность, удаляя точки отказа на основе сеанса, и делает кэширование более предсказуемым. Однако оно также заставляет клиентов обрабатывать аутентификацию и логику повторного использования явно.

Ресурсный

Ресурсы — это фундаментальные абстракции в REST. Ресурс может быть объектом, набором объектов или даже процессом. Каждый ресурс однозначно идентифицируется Единым идентификатором ресурсов (URI). URI должен представлять местоположение ресурса в иерархии. Например, представляет собой набор пользовательских ресурсов, в то время как представляет конкретного пользователя. Операции над ресурсами выполняются с использованием методов HTTP, которые сопоставляются со стандартными действиями CRUD.

Использование HTTP методов

REST использует семантику стандартных методов HTTP единообразно:

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

Представительство

Когда клиент извлекает ресурс, сервер возвращает представление этого ресурса. Наиболее распространенным представлением является JSON, но могут использоваться XML, YAML или даже проприетарные форматы. Представление включает текущее состояние ресурса и может включать в себя ссылки (HATEOAS) на связанные ресурсы. Клиенты взаимодействуют с представлениями, а не сами необработанные ресурсы. API может быть изменен путем изменения формата представления без изменения базового ресурса.

Унифицированный интерфейс

Однородное ограничение интерфейса является наиболее отличительной особенностью REST. Он отделяет клиента от внутренней реализации сервера. Это ограничение состоит из четырех под-ограничений:

Общие вопросы дизайна REST API

Как должны быть структурированы конечные точки?

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

  • — сбор пользователей
  • — один пользователь
  • — заказы, принадлежащие конкретному пользователю
  • — единый заказ

Глубина должна быть ограниченной. Вложение более двух или трех уровней затрудняет чтение и поддержание URI. Для сложных отношений рассмотрите возможность использования параметров запроса или выделенных ресурсов. Избегайте глаголов, таких как , потому что метод HTTP уже передает действие. Последовательность жизненно важна: если вы используете для сбора, не используйте для другой коллекции.

Как справиться с ошибками?

Ответы на ошибки должны быть информативными и последовательными. Используйте правильный код состояния HTTP:

  • 400 Bad Request — некорректный запрос (например, недостающее требуемое поле, недействительный JSON).
  • 401 Несанкционированные — Пропущенные или недействительные учетные данные для аутентификации.
  • 403 Запрещено — Подлинный пользователь не имеет разрешения.
  • 404 Не найдено — Ресурса не существует.
  • 409 Conflict — Запрос конфликтует с текущим состоянием (например, дубликатная запись).
  • 422 Необработанная сущность — ошибки проверки в органе запроса.
  • 500 Внутренняя ошибка сервера — Неожиданный сбой сервера.

В дополнение к коду состояния орган реагирования должен иметь последовательную структуру.

{
 "error": {
 "code": "USER_NOT_FOUND",
 "message": "User with ID 42 not found.",
 "details": "..."
 }
}

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

А как же версия?

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

  • URI-версия — Включите версию в путь (например, ). Это самый популярный подход, потому что он явный и простой в маршрутизации.
  • Редактирование заголовка — Используйте заголовок пользовательского запроса (например, ). Это сохраняет URI чистым, но требует от клиентов правильно установить заголовок.
  • Версия параметров запроса — Добавьте параметр . Обычно это не рекомендуется, потому что он загромождает строки запросов и может мешать кэшированию.

Версия URI является наиболее простой для большинства команд.Сохранить версии в течение разумного периода (не менее двух лет) и обесценить их с четкой связью.

Как реализовать пагинацию, фильтрацию и сортировку?

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

  • Пагинация — Используйте кодирование на основе курсора или офсетное/лимитное.) Легко реализовать, но может стать неэффективным на больших наборах данных. На основе курсора (]) более надежная и последовательная. Включите метаданные кодирования в ответ, такие как , и .
  • Фильтрация — Используйте параметры запроса для логического фильтрования ресурсов. Последовательно применяйте одни и те же шаблоны фильтров в конечных точках.
  • Сортировка — Разрешить сортировку с такими параметрами, как или для убывающего порядка.Документируйте доступные поля сортировки.

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

Как управлять аутентификацией и авторизацией?

API REST не имеют состояния, поэтому аутентификация должна происходить с каждым запросом. Наиболее распространенный подход заключается в использовании маркеров-носителей , передаваемых в заголовке . OAuth 2.0 является отраслевым стандартом безопасности API. Для внутренних API ключи API (проходимые в пользовательском заголовке) иногда достаточны, но они предлагают более слабую безопасность, потому что утечка ключа не может быть легко отменена без изменения самого ключа.

Авторизация (что может сделать пользователь) обычно осуществляется на стороне сервера путем проверки ролей или разрешений, связанных с аутентифицированной личностью. Избегайте встраивания логики авторизации в клиент; всегда проверяйте на сервере.

Императивность

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

Как управлять кэшированием?

Кэширование повышает производительность и снижает нагрузку на сервер. HTTP-кэширование регулируется заголовками, такими как , , и . Для общедоступных API устанавливают соответствующие сроки службы кэша на стабильных ресурсах. Для динамических данных используют условные запросы: клиент отправляет с ETag, а сервер отвечает с , если ресурс не изменился. Это снижает потребление полосы пропускания.

Ненависть или ненависть?

HATEOAS (Hypermedia as the Engine of Application State) часто упоминается как ключевой дифференциатор REST, но на практике он редко полностью принимается. Идея заключается в том, что представление ресурса включает в себя ссылки на связанные действия, позволяя клиентам перемещаться по API без предварительного знания. Например, пользовательский ресурс может включать . Хотя добавление объектов ссылок к вашим ответам не является обязательным, добавление объектов ссылок может сделать API более доступными и уменьшить связь между клиентом и сервером. Начните с простых связей и расширяйте по мере необходимости.

Лучшие практики для REST API Design

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

Последовательность превыше всего

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

Предоставить полную документацию

Хорошая документация является неотъемлемой частью API. Такие инструменты, как Swagger/OpenAPI, Directus (который включает в себя автоматическое создание документации API), и коллекции Postman помогают разработчикам быстро понять ваши конечные точки. Примеры запросов/ответов документов, коды ошибок, ограничения скорости и потоки аутентификации. Сохраняйте документацию синхронизированной с фактическим API.

Используйте стандартные коды состояния HTTP

Никогда не используйте 200 для ошибок или 500 для ошибок клиентов. Правильные коды состояния позволяют клиентам легко обнаруживать успех или сбой программно. Ссылка на код состояния HTTP .

Защищаем каждую конечную точку

Внедряйте аутентификацию и авторизацию на ранней стадии. Используйте исключительно HTTPS. Проверяйте каждый вход на стороне сервера - никогда не доверяйте клиенту. Применяйте ограничение скорости для предотвращения злоупотреблений. Для чувствительных операций требуется дополнительная проверка, такая как маркеры подтверждения или шаблоны, подобные CSRF.

Дизайн для потребителя

Подумайте с точки зрения разработчика, который будет использовать ваш API. Избегайте раскрытия внутренних деталей реализации (например, идентификаторов баз данных в URI). Предоставьте значимые сообщения об ошибках. Предложите портал разработчика или среду песочницы для тестирования. Рассмотрите предложение SDK или клиентских библиотек для популярных языков.

План эволюции

API — это живые продукты. Используйте редактирование, даже если вы не ожидаете прорывных изменений. Избегайте внесения прорывных изменений в незначительные выпуски. Нежно изменяйте конечные точки: добавьте заголовок , указывающий, когда конечная точка будет удалена, и сохраняйте старые версии работоспособными в течение переходного периода.

Заключение

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

Продолжайте изучать руководящие принципы проектирования FLT:0 RESTful API и спецификацию JSON:API JLT:2 для более глубокого понимания. При разработке следующего API учитывайте ограничения безгражданства, ориентации ресурсов и единого интерфейса, а также балансируйте чистоту с прагматизмом. Лучшие API - это те, которые просты, последовательны и уважительны к разработчикам, которые зависят от них каждый день.