REST API для технических специалистов

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

1. Архитектура REST: основные принципы, ресурсы и унифицированный интерфейс

Архитектура REST: основные принципы, ресурсы и унифицированный интерфейс

REST (Representational State Transfer — «передача состояния представления») — это архитектурный стиль взаимодействия компонентов распределенного приложения в сети. В отличие от SOAP, который является протоколом с жестким стандартом, REST представляет собой набор архитектурных ограничений и принципов. Если сервис удовлетворяет этим ограничениям, его называют «RESTful».

Этот стиль был описан Роем Филдингом в 2000 году в его диссертации. Главная цель REST — создание масштабируемых, производительных и надежных веб-сервисов, которые легко поддерживать и развивать.

Ресурс как центральное понятие

Фундаментальной единицей информации в REST является ресурс. Ресурсом может быть что угодно: документ, изображение, временной сервис, коллекция других ресурсов или физический объект (например, конкретный пользователь).

Каждый ресурс должен иметь уникальный идентификатор. В веб-среде таким идентификатором выступает URI (Uniform Resource Identifier). Важно понимать различие между самим ресурсом и его представлением (Representation).

* Ресурс — это абстракция (например, «Пользователь с ID 42»). Он находится на сервере. * Представление — это данные, которые сервер передает клиенту (или клиент серверу) в определенном формате (JSON, XML, HTML), отражающие состояние ресурса в данный момент времени.

!Различие между ресурсом на сервере и его представлением, передаваемым по сети

Когда клиент взаимодействует с API, он не «трогает» сам ресурс напрямую. Он обменивается представлениями этого ресурса, чтобы изменить его состояние на сервере.

Шесть архитектурных ограничений REST

Чтобы система считалась RESTful, она должна соблюдать следующие ограничения:

1. Клиент-сервер (Client-Server)

Система разделена на два независимых компонента: клиент (потребитель данных) и сервер (хранитель данных). Разделение ответственности позволяет развивать клиентскую и серверную части независимо друг от друга. Клиент не заботится о хранении данных, а сервер не заботится о пользовательском интерфейсе.

2. Отсутствие состояния (Stateless)

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

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

Почему это важно: * Масштабируемость: Серверу не нужно тратить ресурсы на поддержание открытых соединений или хранение сессий в памяти. * Надежность: Если один сервер падает, запрос клиента может быть перенаправлен на другой сервер без потери контекста, так как контекст передается в самом запросе.

3. Кэшируемость (Cacheable)

Данные в ответе сервера должны быть явно помечены как кэшируемые или некэшируемые. Если ответ кэшируемый, клиент (или промежуточный прокси) может использовать эти данные для будущих запросов, не обращаясь к серверу повторно. Это снижает нагрузку на сеть и уменьшает задержки.

4. Единообразие интерфейса (Uniform Interface)

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

5. Слоистая система (Layered System)

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

!Пример слоистой системы, где клиент взаимодействует с сервером через посредников

6. Код по требованию (Code on Demand) — опционально

Сервер может временно расширять функциональность клиента, передавая ему исполняемый код (например, JavaScript-скрипты или апплеты). Это единственное необязательное ограничение REST.

Унифицированный интерфейс (Uniform Interface)

Единообразие интерфейса достигается за счет четырех принципов:

Идентификация ресурсов

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

Примеры: * https://api.example.com/orders — коллекция заказов. * https://api.example.com/orders/123 — конкретный заказ.

Плохой пример (использование глаголов в URI): * https://api.example.com/getOrders * https://api.example.com/createOrder

Манипуляция ресурсами через представления

Если у клиента есть представление ресурса (включая метаданные), у него достаточно информации для модификации или удаления этого ресурса на сервере (при наличии прав доступа). Мы используем стандартные методы HTTP (GET, POST, PUT, DELETE) для выполнения действий над ресурсами, указанными в URI.

Самоописываемые сообщения (Self-descriptive messages)

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

  • Метода запроса: Говорит о том, что нужно сделать (GET — получить, DELETE — удалить).
  • Заголовков (Headers): Например, Content-Type: application/json сообщает парсеру, что тело запроса — это JSON-объект.
  • Кодов ответа: Сообщают результат операции (200 OK, 404 Not Found).
  • Пример самоописываемого ответа:

    Здесь Content-Type объясняет формат данных, а Cache-Control инструктирует клиента о правилах кэширования.

    HATEOAS (Hypermedia as the Engine of Application State)

    Это наиболее сложный для внедрения принцип. Он означает, что клиент взаимодействует с приложением исключительно через гипермедиа, предоставляемую сервером динамически.

    Вместо того чтобы клиент жестко «зашивал» в коде логику переходов (например, знание о том, что после создания заказа нужно идти на /payment), сервер сам присылает доступные ссылки для следующих действий в ответе.

    Пример ответа с HATEOAS:

    Если баланс станет отрицательным, сервер просто не пришлет ссылку withdraw, и клиентский интерфейс (если он построен правильно) автоматически скроет кнопку вывода средств. Это позволяет менять логику на сервере, не обновляя клиент.

    Итоги

  • REST — это стиль, а не стандарт. Он задает набор ограничений для построения гибких систем, но не диктует жестких протоколов реализации.
  • Ресурс и Представление разделены. Клиент работает с JSON или XML представлением, а не с записью в базе данных напрямую.
  • Stateless (безгражданственность) критична. Сервер не помнит клиента между запросами; каждый запрос самодостаточен. Это ключ к горизонтальному масштабированию.
  • Унифицированный интерфейс. Использование правильных URI, стандартных HTTP-методов и самоописываемых сообщений делает API предсказуемым и простым для интеграции.
  • 2. Протокол HTTP: методы запросов, семантика и коды состояния ответа

    Протокол HTTP: методы запросов, семантика и коды состояния ответа

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

    HTTP (HyperText Transfer Protocol) — это протокол прикладного уровня, работающий по модели «клиент-сервер». Обмен данными происходит через сообщения: запросы (Requests) от клиента и ответы (Responses) от сервера.

    Структура HTTP-сообщения

    Понимание анатомии сообщения необходимо для отладки и разработки. Любое сообщение состоит из стартовой строки, заголовков и опционального тела.

    !Анатомия HTTP-сообщений: запрос и ответ

    HTTP-запрос

  • Стартовая строка (Request Line): Содержит метод (GET), цель запроса (URI) и версию протокола.
  • Заголовки (Headers): Метаданные запроса (например, User-Agent, Authorization).
  • Тело (Body): Данные, отправляемые на сервер (присутствует в POST, PUT, PATCH).
  • HTTP-ответ

  • Стартовая строка (Status Line): Версия протокола, числовой код состояния и текстовое пояснение.
  • Заголовки: Метаданные ответа (например, Content-Type, Set-Cookie).
  • Тело: Запрашиваемые данные или описание ошибки.
  • Методы HTTP (Verbs)

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

    GET

    Используется для получения представления ресурса. Запросы GET не должны содержать тело (payload) и не должны изменять состояние сервера (только чтение).

    Пример: GET /users/123

    POST

    Используется для создания нового подчинённого ресурса в коллекции или для обработки данных процессом, определённым ресурсом. Сервер сам назначает идентификатор новому ресурсу.

    Пример: POST /users (создает нового пользователя, ID присваивает база данных).

    PUT

    Используется для полной замены или создания ресурса, если клиент знает его идентификатор. Клиент отправляет полное представление ресурса. Если ресурс существует — он перезаписывается, если нет — создается.

    Пример: PUT /users/123 (обновляет данные пользователя 123 целиком).

    PATCH

    Используется для частичного изменения ресурса. Клиент отправляет только те поля, которые нужно изменить.

    Пример: PATCH /users/123 (с телом {"email": "new@example.com"}).

    DELETE

    Используется для удаления ресурса по указанному URI.

    Пример: DELETE /users/123

    HEAD

    Аналогичен GET, но сервер возвращает только заголовки без тела ответа. Полезно для проверки существования ресурса или его метаданных (размер, дата изменения) без скачивания контента.

    OPTIONS

    Используется для получения параметров взаимодействия с ресурсом. Часто применяется в CORS (Cross-Origin Resource Sharing) для проверки разрешенных методов и заголовков.

    Семантика методов: Безопасность и Идемпотентность

    Техническому специалисту важно различать эти два понятия, так как они влияют на стратегию обработки ошибок и повторных запросов (retry policy).

    Безопасные методы (Safe Methods)

    Метод считается безопасным, если он не изменяет состояние ресурса на сервере. Это операции «только для чтения». Клиент может выполнять их без риска повредить данные.

    * Безопасные: GET, HEAD, OPTIONS. * Небезопасные: POST, PUT, PATCH, DELETE.

    Идемпотентные методы (Idempotent Methods)

    Метод считается идемпотентным, если многократное повторение одного и того же запроса приводит к тому же состоянию сервера, что и однократное выполнение. Это свойство критично при нестабильной сети: если клиент не получил ответ, он может смело повторить идемпотентный запрос.

    Математически это можно выразить так:

    где — это функция (HTTP-метод), применяемая к состоянию системы . Результат двойного применения функции равен результату её однократного применения.

    Разберем на примерах:

  • DELETE (Идемпотентный):
  • * Запрос 1: DELETE /users/123 -> Удаляет пользователя. Ответ 200 OK. * Запрос 2: DELETE /users/123 -> Пользователя уже нет. Состояние сервера не изменилось (он все еще удален). Ответ 404 Not Found (код ответа может отличаться, но состояние сервера то же).

  • PUT (Идемпотентный):
  • * Запрос 1: PUT /users/123 (set name="Alex") -> Имя стало Alex. * Запрос 2: PUT /users/123 (set name="Alex") -> Имя осталось Alex.

  • POST (НЕ идемпотентный):
  • * Запрос 1: POST /orders -> Создан заказ №1. * Запрос 2: POST /orders -> Создан заказ №2 (дубликат).

    Таблица свойств методов:

    | Метод | Безопасный | Идемпотентный | | :--- | :--- | :--- | | GET | Да | Да | | HEAD | Да | Да | | PUT | Нет | Да | | DELETE | Нет | Да | | POST | Нет | Нет | | PATCH | Нет | Нет (обычно)* |

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

    Коды состояния HTTP (Status Codes)

    Код состояния — это трехзначное число, сообщающее результат обработки запроса. В REST API правильное использование кодов заменяет собственные «обертки» ошибок в JSON.

    2xx: Успех (Success)

    * 200 OK: Стандартный ответ для успешных HTTP-запросов (GET, PUT, PATCH). * 201 Created: Ресурс был успешно создан. Обычно возвращается в ответ на POST или PUT. В заголовке Location сервер должен вернуть ссылку на созданный ресурс. * 204 No Content: Запрос выполнен успешно, но тела ответа нет. Часто используется для DELETE или PUT (если обновление не требует возврата данных).

    3xx: Перенаправление (Redirection)

    * 301 Moved Permanently: Ресурс перемещен навсегда. Клиент должен использовать новый URI. * 304 Not Modified: Используется для кэширования. Говорит клиенту, что версия ресурса у него в кэше все еще актуальна и тело передавать не нужно.

    4xx: Ошибки клиента (Client Error)

    Вина лежит на клиенте (неверные данные, нет прав).

    * 400 Bad Request: Общая ошибка. Синтаксическая ошибка в запросе, невалидный JSON и т.д. * 401 Unauthorized: Ошибка аутентификации. Клиент не представился (нет токена или он неверный). Смысл: «Я не знаю, кто ты». * 403 Forbidden: Ошибка авторизации. Клиент аутентифицирован, но у него нет прав на это действие. Смысл: «Я знаю, кто ты, но тебе сюда нельзя». * 404 Not Found: Ресурс не найден. * 405 Method Not Allowed: Метод, указанный в строке запроса, нельзя применить к этому ресурсу (например, POST к эндпоинту, поддерживающему только GET). * 429 Too Many Requests: Превышен лимит запросов (Rate Limiting).

    5xx: Ошибки сервера (Server Error)

    Вина лежит на сервере. Клиентский запрос валиден, но сервер не смог его обработать.

    * 500 Internal Server Error: Общая ошибка сервера (например, исключение в коде, падение базы данных). * 502 Bad Gateway: Сервер, выступая в роли шлюза или прокси, получил недействительный ответ от вышестоящего сервера. * 503 Service Unavailable: Сервер временно не готов обрабатывать запрос (перегрузка или техническое обслуживание).

    Итоги

  • Методы имеют значение: Используйте GET для чтения, POST для создания, PUT для замены, PATCH для модификации и DELETE для удаления. Это делает API предсказуемым.
  • Идемпотентность важна для надежности: Понимая, какие методы идемпотентны (PUT, DELETE), вы можете безопасно реализовывать логику повторных запросов (retries) при сбоях сети.
  • Коды состояния — это контракт: Не возвращайте всегда 200 OK с ошибкой внутри JSON. Используйте 4xx для ошибок клиента и 5xx для ошибок сервера, чтобы системы мониторинга и клиенты могли правильно реагировать.
  • Разделяйте 401 и 403: 401 требует от пользователя входа в систему, 403 сообщает о недостаточности прав у уже вошедшего пользователя.