Вопрос 14 · Раздел 6

Что такое HATEOAS?

Без HATEOAS:

Версии по языкам: English Russian Ukrainian

Junior Level

HATEOAS (Hypermedia As The Engine Of Application State) — это принцип REST, при котором сервер возвращает не только данные, но и ссылки на возможные действия.

Простой пример:

Без HATEOAS:

{
  "id": 101,
  "status": "NEW"
}

С HATEOAS:

{
  "id": 101,
  "status": "NEW",
  "_links": {
    "self": { "href": "/orders/101" },
    "payment": { "href": "/orders/101/pay" },
    "cancel": { "href": "/orders/101/cancel" }
  }
}

Зачем это нужно?

  • Клиент не должен “знать” URLs — он просто следует по ссылкам
  • Если сервер изменит URLs, клиент не сломается
  • API становится самодокументируемым

Аналогия:

Это как веб-сайт: вы не знаете все URL наизусть, вы просто кликаете по ссылкам на странице.


Middle Level

Концепция Affordances (Возможности)

В HATEOAS ссылки — это не просто URL, это Affordances (возможности):

  • Affordance (в контексте HATEOAS) — подсказка сервера клиенту о том, какие действия сейчас возможны. Как кнопка на веб-странице «подсказывает», что её можно нажать. Если сервер не вернул ссылку rel="delete" — значит, удаление сейчас невозможно.
  • Если у пользователя нет прав на удаление — сервер просто не пришлёт ссылку rel="delete"
  • Клиенту не нужно дублировать бизнес-логику проверки прав
  • Достаточно: if (response.links.delete) showDeleteButton()

Стандарт HAL (Hypertext Application Language)

Самый популярный формат. Использует поле _links:

{
  "id": 101,
  "status": "NEW",
  "_links": {
    "self": { "href": "/orders/101" },
    "payment": { "href": "/orders/101/pay" },
    "cancel": { "href": "/orders/101/cancel" }
  }
}

Spring HATEOAS

Библиотека использует RepresentationModel и LinkDiscoverer:

  • WebMvcLinkBuilder строит ссылки динамически, считывая маппинги контроллеров
  • Проблема: За прокси (Nginx/API Gateway) ссылки могут содержать внутренний IP
  • Решение: Заголовки X-Forwarded-* и настройка ForwardedHeaderFilter

Производительность

  • Payload Overhead: Для одного ресурса оверхед 30-50%. Для коллекций из 100+ элементов — до 2-3x, так как каждая запись содержит свои ссылки.
  • Оптимизация: Ссылки только в детальное представление (Single Resource), в списках — только self
  • CPU Cost: Генерация ссылок для 1000 элементов требует ресурсов (рефлексия в Spring)

Edge Cases

  • Версионирование: Если изменили путь к эндпоинту оплаты, клиент, использующий HATEOAS, не заметит — он берёт URL из ссылки. Это обеспечивает идеальную обратную совместимость
  • Client Caching: Кэширование ответов с HATEOAS может быть опасным, если ссылки зависят от прав пользователя. Если HATEOAS-ответы кешируются и ссылки зависят от прав пользователя, добавьте Vary: Authorization, иначе CDN может отдать кешированный ответ с чужими ссылками.

Senior Level

Когда НЕ использовать HATEOAS

  1. Внутренние микросервисы — оверхед без пользы
  2. Мобильные приложения с фиксированными экранами — URL не меняются
  3. Highload-системы — где каждый байт на счету
  4. Клиент и бэкенд одной команды — coupling уже минимален

HATEOAS как State Machine

HATEOAS реализует паттерн State Machine на уровне сетевого протокола:

  • Клиент — “тупой” исполнитель, который переходит по ссылкам
  • Сервер управляет состоянием приложения через доступные ссылки
  • Это радикально снижает coupling между фронтендом и бэкендом

Spring HATEOAS (Under the hood)

  • RepresentationModel — базовый класс для ресурсов со ссылками
  • LinkDiscoverer — парсит и находит ссылки в ответах
  • Проблема с прокси: WebMvcLinkBuilder строит ссылки на основе внутренних маппингов. За Nginx/API Gateway ссылки содержат внутренний IP

Производительность и Highload

Payload Overhead

В Highload-системах HATEOAS может быть проблемой:

  • Для одного ресурса оверхед 30-50%. Для коллекций из 100+ элементов — до 2-3x
  • CPU на генерацию ссылок для каждой записи (рефлексия)

Оптимизации

  1. Ссылки только в Single Resource, в Collections — только self
  2. Компактные форматы ссылок (минимальные rel имена)
  3. Кэширование шаблонов ссылок

Диагностика и Мониторинг

  • Discoverability: Можно “гулять” по API через HAL Browser, кликая по ссылкам
  • Broken Links: Мониторинг должен проверять валидность генерируемых ссылок. Если self ведёт на 404 — баг генератора
  • Vary Header: Если HATEOAS-ответы кешируются и ссылки зависят от прав пользователя, добавьте Vary: Authorization, иначе CDN может отдать кешированный ответ с чужими ссылками.

🎯 Шпаргалка для интервью

Обязательно знать:

  • HATEOAS = Hypermedia As The Engine Of Application State — сервер возвращает данные + ссылки на действия
  • Клиент не должен знать URLs — он следует по ссылкам из ответов сервера
  • Affordances — подсказки сервера о доступных действиях; нет ссылки rel="delete" = удаление невозможно
  • Стандарт HAL использует поле _links с self, payment, cancel и т.д.
  • Payload Overhead: 30-50% для одного ресурса, до 2-3x для коллекций из 100+ элементов
  • HATEOAS не нужен для: внутренних микросервисов, mobile приложений, highload-систем
  • Spring HATEOAS: RepresentationModel + WebMvcLinkBuilder; проблема с прокси решается через X-Forwarded-*

Частые уточняющие вопросы:

  • Что такое Affordance в контексте HATEOAS? — Подсказка сервера о доступных действиях (как кнопка на странице)
  • Почему HATEOAS увеличивает размер JSON? — Каждая запись содержит ссылки; для коллекций оверхед до 2-3x
  • Как решить проблему ссылок за прокси? — Заголовки X-Forwarded-* и настройка ForwardedHeaderFilter в Spring
  • Когда HATEOAS точно не нужен? — Внутренние микросервисы, mobile с фиксированными экранами, highload

Красные флаги (НЕ говорить):

  • «HATEOAS нужен в каждом проекте» — избыточен для внутренних API и mobile
  • «HATEOAS — это просто ссылки в JSON» — это ещё и State Machine и управление coupling
  • «Ссылки HATEOAS можно кешировать без Vary: Authorization» — CDN отдаст чужие ссылки другому пользователю
  • «HATEOAS уменьшает размер ответа» — увеличивает на 30-50% для одного ресурса

Связанные темы:

  • [[Что такое REST]]
  • [[Что такое RESTful API дизайн]]
  • [[Стоит ли использовать глаголы в URL]]
  • [[Как организовать версионирование REST API]]
  • [[Что такое Accept header]]