REST — принципы построения API

17 марта 2018
В 2000 году один из авторов спецификаций протокола HTTP Рой Филдинг (Roy T. Fielding) в своей диссертации «Архитектурные стили и проектирование сетевых программных архитектур» (https://roy.gbiv.com/pubs/dissertation/top.htm) предложил принципы обмена данными, объединив их в архитектурный подход под названием «Representational State Transfer (REST)». В русском языке нет прямого перевода этого названия, близким по смыслу будет «Передача представлений». Ссылка на главу диссертации Роя с описанием REST https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm

Слово «представление» тут используется в значении набора данных, которых будет достаточно для обработки запроса сервером и достаточно для понимания ответа на стороне клиента. В случае http-запроса представление включает в себя http-сообщение целиком, а именно: стартовую строку, включающую метод запроса GET/POST/DELETE/PUT/PATCH, адрес, версию протокола, заголовок и тело с данными.

Принципы REST

REST как архитектурный подход для построения систем обмена данных призывает использовать следующие принципы:

1) Разработка с нуля (Null Style)

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

2) Клиент-серверная архитектура (Client-Server)

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

3) Отсутствие сохранения состояния (Stateless)

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

4) Кеширование ответов (Cache)

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

5) Унификация интерфейса (Uniform Interface) 

Важный принцип REST, направленный на стандартизацию REST-архитектуры. Его ещё переводят как единый интерфейс, но этот перевод некорректный по сути вещей.
Принцип состоит из четырёх групп правил: 
  • Идентификация ресурсов
    Обращение к ресурсам сервера должно осуществляться с помощью URI-адресов. Идентификатор строится в следующем формате:
    <протокол>://<домен-сервиса>/<тип-ресурса>/<id-ресурса>/

    Пример идентификатора
    https://example.com/customers/12345/
  • Манипулирование ресурсами посредством представлений
    Для получения или изменения данных используются представления. Представление в понимании REST-архитектуры и при использовании HTTP-протокола означает http-запрос.
  • Самоописательные сообщения
    Запросы и ответы должны содержать все необходимые сведения для понимания получателем присланных данных. При http-обмене одним из параметров описания формата сообщения является свойство Content-Type. К примеру, у сообщения с данными в формате JSON заголовок сообщения будет содержать Content-Type: application/json.
  • Гипермедиа как двигатель состояния приложения (сокращённо HATEOAS)
    Сервер в ответе должен присылать адреса на связанные с запрошенным ресурсом действиями и другими ресурсами. Таким образом, каждый ответ содержит карту для дальнейшего взаимодействия с сервером и выступает двигателем запросов.

    Ниже пример ответа, который в поле links содержит адреса для совершения действий над балансом пользователя. Поле links, включая своё имя, может иметь любой формат и содержимое, строгих правил нету. Единственное, обратная сторона должна быть готова принять эти данные.
    {
        "balance": {
            "amount": 95000000,
            "currency": "RUB"
        },
        "links": {
            "self": "/accounts/1/",
            "update": "/accounts/1/",
            "delete": "/accounts/1/",
            "invoices": "/accounts/1/invoices/",
            "close": "/accounts/1/close/"
        }
    }

6) Многоуровневая система (Layered System)

Подразумевается, что каждый компонент системы обмена данных взаимодействует только с соседним уровнем компонентов. Разработчик Дэн Мартенсен у себя на сайте https://danmartensen.svbtle.com/exploring-rest-api-architecture наглядно изобразил пример 4-х уровневой системы, которая включает: клиентский уровень, уровень веб-сервера, уровень API с кэшированием и уровень данных. Каждый из этих уровней взаимодействует только со своими смежными уровнями. Между уровнями могут быть прослойки в виде посредников, таких как прокси-серверы, балансировщики нагрузки, брандмауэры (сетевые экраны) и другие.

 

7) Код по требованию (Code-On-Demand)

Cервер может передавать в ответе клиенту код для выполнения, например, в браузере пользователя. В случае с браузером обычно это код на Java Script.

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

REST призывает для выполнения запросов на получение данных, их изменение и удаление использовать соответствующие HTTP-методы. 

Таблица методов с описанием возможных действий
Метод Действие (CRUD) Возможные статусы ответа
POST Создание (Create) 201 (запись создана), 404 (ресурс не найден), 409 (возник конфликт. например, если запись уже создана)
GET Чтение, получение данных (Read) 200 (успешное выполнение действия), 404 (ресурс не найден)
PUT Изменение через замену всего объекта новыми данными (Update as replace) 200 (успешное выполнение ), 404 (ресурс не найден), 405 (метод запрещён)
PATCH Простое изменение (Update as modification) 200 (успешное выполнение ), 404 (ресурс не найден), 405 (метод запрещён)
DELETE Удаление (Delete) 200 (успешное выполнение ), 404 (ресурс не найден), 405 (метод запрещён)


Пример REST-запросов

1) Получение информации о пользователе
GET http://example.com/customers/12345/

2) Изменение данных пользователя
PATCH http://example.com/customers/12345/
{
    "name": "Пётр",
    "surname": "Иванов",
    "age": "23"
}

Пример REST-ответов

1) Сведения о пользователе
{
    "name": "Пётр",
    "surname": "Иванов",
    "middle_name": "Иванович",
    "age": "23",
    "position": "складской менеджер",
    "email": "petr-ivanov@example.com",
    "telephone_in_company": "73",
    "links": {
        "self": "/customers/12345/",
        "update": "/customers/12345/",
        "delete": "/customers/12345/",
        "send_message": "/customers/12345/message/"
    }
}

2) Изменение данных пользователя
{
    "status_code": "success",
    "status_message": "Сохранение выполнено"
}

Замечание

REST API, которые сделаны с соблюдением всех принципов попадаются крайне редко. Я даже не могу с ходу привести пример такого API. Обычно всё ограничивается URI-адресацией, методами GET, POST, DELETE и обменом данными в формате JSON.