В этой статье вы продолжите изучать REST API и узнаете о HATEOAS.

При просмотре веб-страницы вы можете просматривать данные на странице и выполнять с ними действия.

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



Вы изучите:


  • Что такое HATEOAS?
  • Почему вам нужно использовать HATEOAS?
  • Когда использовать HATEOAS?

REST API


Это пятая статья в серии статей про REST API:


Что означает HATEOAS?


Термин HATEOAS означает фразу «Hypermedia As The Engine Of Application State» (Гипермедиа как двигатель состояния приложения). Чтобы понять это глубже, нам сначала нужно понять значение гипермедиа. Взгляните на следующую веб-страницу:



Когда браузер загружает страницу, вы определенно можете увидеть весь контент, который может предложить эта страница. Что еще более интересно, страница также позволяет вам выполнять множество действий с этими данными, например:

  • Нажатие на кнопки (зеленое «Clone» (Клонировать) или «Download» (Скачать)
  • Нажатие на вкладки (например, для просмотра «Issues» (Проблемы))
  • и еще несколько

Теперь давайте посмотрим, как ведут себя наши REST API:

Если вы посмотрите на типичный запрос GET к RESTful серверу, такой как этот:



Запрос GET localhost:8080/users получает набор данных трех пользователей в этом случае. Отправив запрос с помощью GET localhost:8080/users/1, вы получите сведения только о первом пользователе. Как правило, когда мы выполняем запрос REST, мы получаем только данные, а не какие-либо действия с ними. Вот где HATEOAS восполняет пробел. Запрос HATEOAS позволяет вам не только отправлять данные, но и указывать связанные действия:



Этот пример был в формате JSON. Формат XML для другого примера будет выглядеть примерно так:



Когда вы отправляете этот запрос для получения данных учетной записи, вы получаете оба:

  • Номер счета и данные баланса
  • Ссылки, которые обеспечивают действия, чтобы сделать депозит/снятие/перевод/закрытие

С HATEOAS запрос на REST ресурс дает мне как данные, так и действия, связанные с данными.

Зачем нам нужен HATEOAS?


Единственная самая важная причина для HATEOAS — слабая связь (loose coupling). Если потребителю службы REST необходимо жестко закодировать все URL-адреса ресурсов, он тесно связан с реализацией вашей службы. Вместо этого, если вы вернете URL-адреса, которые он может использовать для действий, он будет слабосвязанным. Нет строгой зависимости от структуры URI, так как она указана и используется в ответе. Несколько важных тем, связанных с HATEOAS:

HAL — язык гипертекстовых приложений


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



С HAL у вас есть несколько категорий представлений:

  • Links (Ссылки): указано как комбинация
  • Target (Цель) — указана в качестве URI
  • Relation (Отношение) — имя
  • Embedded Resources (Встроенные ресурсы): другие ресурсы, содержащиеся в данном REST ресурсе
  • State (Состояние): фактические данные ресурса

Если вам довелось использовать Spring Framework для разработки REST сервиса, то Spring HATEOAS — хороший механизм для вашего сервиса.

По этому вопросу имеется авторское видео.

Резюме


В этой статье мы рассмотрели, что означает HATEOAS.

С помощью HATEOAS ответы на запросы REST возвращают не только данные, но и действия, которые можно выполнить с ресурсом.

Это помогает сделать приложения слабосвязанными.

Дополнительное чтение


REST + MongoDB + HATEOAS?

HATEOAS REST Services With Spring

Spring HATEOAS: Hypermedia APIs with Spring (добавлено переводчиком)

Комментарии (23)


  1. endymion
    10.01.2020 12:43

    Сколько лет я читаю про HateUs — до сих пор не видел ни одного логичного объяснения зачем оно нужно с чисто практической точки зрения.


    Очень много воды про "слабосвязанные компоненты" и прочую чепуху. Но, пардон, rest client — это не человек. Ему не нужно эмпирически выяcнять куда кликнуть, чтобы получить результат.


    1. arthuriantech
      10.01.2020 13:17

      HATEOAS является обязательной частью REST с самого начала. Ваш браузер работает на этом принципе.


      1. maxzh83
        10.01.2020 13:43
        +4

        Хочется подробностей. На каком именно принципе работает браузер? Почему HATEOAS является обязательной частью REST с самого начала?
        HATEOAS встречал хорошо если в 1% от всех rest api и пользы оно там немного приносило, если честно.


        1. arthuriantech
          10.01.2020 14:43

          Тема очень обширна и на самом деле заслуживает отдельной статьи, но я по мере своих сил перескажу суть. REST ведет свою историю еще с 1994 года, когда Рой Филдинг был привлечен к работе над HTTP/1.0 и URI, став потом основным архитектором HTTP/1.1. В то время он формализовал концепции, которые использовались при проектировании веба, и затем, в 2000 году, REST в его законченном виде стал предметом докторской диссертации Филдинга.
          «The name "Representational State Transfer" is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use.»
          https://www.ics.uci.edu/~fielding/pubs/dissertation/evaluation.htm


          Акроним HATEOAS является ничем иным, как отрывком из четвертого, центрального ограничения REST — Uniform Interface: «REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.». Практически весь современный веб, каким мы его знаем, основан на этом. Браузеры работают на гипертексте — они не содержат захардкоженных ссылок, а следуют ссылкам, которые были получены из ответов сервера, руководствуясь медиа-типами и другими метаданными. Именно из-за этого ограничения REST-клиенты похожи на браузеры.


          «What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period.»
          Roy Fielding (REST APIs must be hypertext-driven)


          Сейчас (на само деле давно) REST является модным ярлыком, который навешивается практически на любой HTTP API, даже если авторы понятия не имеют, что это значит.


          https://www.youtube.com/watch?v=w5j2KwzzB-0
          https://www.youtube.com/watch?v=pspy1H6A3FM&t=17m16s
          https://roy.gbiv.com/untangled/tag/rest
          https://www.infoq.com/articles/web-api-rest/
          https://www.infoq.com/news/2016/07/microsoft-rest-api/
          https://github.com/Microsoft/api-guidelines/pull/29
          https://tyk.io/rest-never-crud/
          https://stackoverflow.com/questions/19884295/soap-vs-rest-differences/19884975#19884975
          https://twitter.com/fielding/status/1052976631374000128


          1. endymion
            10.01.2020 14:53

            Все это здорово, но я опять повторю вопрос: с практической точки зрения зачем оно нужно?


            1. arthuriantech
              10.01.2020 15:47

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


          1. maxzh83
            10.01.2020 16:17

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

            Браузеры работают на гипертексте — они не содержат захардкоженных ссылок, а следуют ссылкам, которые были получены из ответов сервера

            Во-первых, логично, что браузер (просмотровщик) не содержит ничего захардкоженного. Примерно как текстовый редактор не содержит захардкоженного текста. Во-вторых, вы не браузер разрабатываете, а веб приложение, т.е. текстовый документ, а не текстовый редактор, если брать такую аналогию. В этом приложении есть клиентская и серверная части. И вот тут мне видится попытка переложить на серверную часть то, что должен делать клиент.
            Простой пример, у меня есть клиент и несколько серверов (нод или экземпляров микросервисов), перед которыми стоит балансировщик, какую ссылку должен вернуть сервер? На балансировщик? или без ничего? Еще вопрос, должен ли HATEOAS описывать данные, которые требуется передать, например в body?


            1. Cykooz
              10.01.2020 16:49

              какую ссылку должен вернуть сервер?

              Сервер отдаёт либо ссылку на тот же домен на который пришёл запрос (например домен балансировщика), или на что-либо другое в соответствии с какой-то внутренней логикой.

              должен ли HATEOAS описывать данные

              HATEOAS по определению не описывает то, что клиент должен делать с ссылкой.

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

              Ссылки на ресурсы — это первое что «страдает» в процессе масштабирования. Их приходится менять, что бы перераспределить нагрузку на серверы. Единый балансировщик на одном домене не является «серебряной пулей» в вопросах распределения нагрузки.


              1. maxzh83
                10.01.2020 18:32

                Основная цель REST — разработка масштабируемой распределённой архитектуры, а не протокол описания API

                Статья называется "REST API— Что такое HATEOAS?"
                А вообще речь не про абстрактный REST, а про HATEOAS и конкретные примеры его использования.


                1. arthuriantech
                  10.01.2020 18:37

                  Что такое абстрактный REST?


                  1. maxzh83
                    10.01.2020 18:44

                    Забудьте про абстрактный, неправильно выразился. Речь просто не про REST, а про HATEOAS


            1. arthuriantech
              10.01.2020 18:10

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

              Базис запросто может оставаться актуальным в масштабе десятилетий без значительных изменений. Парадигмы, языки программирования, шаблоны проектирования, протоколы, спецификации, API операционных систем. Язык С был языком 2017 года по версии TIOBE, хотя на тот момент ему было более сорока лет.


              Во-вторых, вы не браузер разрабатываете, а веб приложение, т.е. текстовый документ, а не текстовый редактор, если брать такую аналогию.

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


              Простой пример, у меня есть клиент и несколько серверов (нод или экземпляров микросервисов), перед которыми стоит балансировщик, какую ссылку должен вернуть сервер? На балансировщик? или без ничего?

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


              Еще вопрос, должен ли HATEOAS описывать данные, которые требуется передать, например в body?

              Схематически. Например, в HTML-формах указывается информация, отвечающая за формирование ссылки и формат данных. В случае с JSON сервер может передать описание формы через JSON Schema. В целом, это достаточно сложный вопрос. Единого решения здесь нет и не предвидится.


              1. maxzh83
                10.01.2020 18:40

                Схематически.

                Здорово. Я просто к тому, что вот клиент получил ссылку, ему не надо ее хардкодить — все хорошо. Но потом ему надо ее вызвать, т.е. надо знать что это за метод (POST, DELETE и т.д.) и еще неплохо знать, что отправить. А REST, как мудрый филин, ничего об этом не говорит и вообще он не про это, он о высоком.
                Простой пример, фильтр по параметрам, довольно часто это делают POST и передают json с выбранными параметрами фильтрации. И теперь получается, что к конкретной ссылке будет прибит гвоздями объект фильтрации.
                В случае с JSON сервер может передать описание формы через JSON Schema

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


                1. arthuriantech
                  10.01.2020 19:03

                  Это работает аналогично HTML-формам. Например, посмотрите на HAL формы (https://rwcbook.github.io/hal-forms/). Метод указывается явно или подразумевается из relation. Ссылка или указана явно, или может сформирована клиентом при использовании URI Template. Их хардкодить не нужно.


        1. Cykooz
          10.01.2020 14:52

          HATEOAS встречал хорошо если в 1% от всех rest api

          Мне кажется что API, которым можно хотя бы с натяжкой повесить ярлык REST на самом деле очень мало. И если многие разработчики даже не смогли дотянуть до этого уровня, то какой уж там HATEOAS — естественно что редко его встретишь. Но если формально подойти к вопросу то, как указал выше arthuriantech, весь Web на базе HTTP-HTML — это самый настоящий REST, в котором даже HATEOAS есть. И это уже совсем не 1%.


    1. apapacy
      10.01.2020 13:52

      Мне понравился один доклад в котором это объяснялось. Представьте что Вы работаете с текстом. В тексте встречаются гиперссылки (то есть вы не забиваете адреса документов вручную а кликаете по ссылке).
      Аналогично с HateUs. Вы публикуете некий рут Ваших API из которого получаете ссылки на запросы других API. Таким образом если Вы рарабатываете мобильное приложение Вы имеете возможность менять API не деплоя новую версию мобильного приложения. Т.к. все URL RESTAPI Вы получаете с сервера.


      Ссылку на это доклад привожу https://www.youtube.com/watch?v=MmyvNUAnI64


      1. endymion
        10.01.2020 14:51

        Не поверю пока не увижу. Если я поменяю API, то клиент тупо упадет.


        1. Cykooz
          10.01.2020 15:25

          А если автор этой статьи поменяет в ней ссылки на предыдущие части цикла, перенаправит их на копию в своём личном блоге, пользователи, при переходе по ним, тоже «тупо упадут» потеряв навыки чтения?

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

          Фактически клиентам вообще нет ни какого прока от URL-ов зашитых в их исходниках, в них почти нет ничего для них полезного — это в общем случае просто какой-то текущий адрес ресурса. Код клиентов работает с самими ресурсами, а не ссылками на них. В концепциях REST правильнее для получения ресурсов пользоваться более высокоуровневыми абстракциями, а не ссылками, захардкожеными в исходниках.


          1. arthuriantech
            10.01.2020 15:55

            Совершенно верно. Филдинг указывает на это в своей диссертации.


            «At no time whatsoever do the server or client software need to know or understand the meaning of a URI — they merely act as a conduit through which the creator of a resource (a human naming authority) can associate representations with the semantics identified by the URI.»
            https://www.ics.uci.edu/~fielding/pubs/dissertation/evaluation.htm#sec_6_2_4


            Если копнуть немного глубже, можно узнать очень много нового)


    1. onegreyonewhite
      10.01.2020 13:56

      Я вот тоже не могу понять. Для построения клиента есть OpenAPI/CoreAPI. На крайняк просто формируешь ответ на HEAD или OPTIONS. А для приложений, которые интегрируются с этим интерфейсом, эта вся информация только лишняя нагрузка на сеть. Данных на сотню байт не наберётся, а для описания действий с ними ещё раз 10 по столько же. И ладно если раз запросил и используешь, но на каждый чих всю структуру вываливать как-то… стремно что ли.


    1. Cykooz
      10.01.2020 14:22

      Всё очень просто — клиенту не надо хардкодить ссылки, ему надо хардкодить relation-ы, т.е. некие имена/идентификаторы тех ссылок, которые ему нужны.
      Зная имя ссылки на какое-то действие или связанный ресурс, саму ссылку клиент получает из ответа сервера. Остаётся только правильно разместить все эти ссылки в ответах сервера, что бы клиент, начав с некоего одного «захардкоженного» URL-а (например корень API), смог оптимально получать нужные ему ссылки без лишних запросов.
      Такой подход позволяет не меняя код клиентов вносить изменения в ссылки:
      — менять в них query-string;
      — менять расположение ресурсов, вплоть до изменения домена (например для распределения нагрузки).

      PS: HAL — это один из вариантов реализации HATEOAS, в котором гиперссылки передаются в теле ответа в специальном поле. При желании можно для получения гиперссылок использовать упомянутые выше OPTIONS запросы. Только в этом случае придётся озаботится вопросами консистентности, т.к. между GET и OPTIONS запросами состояние ресурса на сервере может изменится.


  1. Demtriy
    10.01.2020 15:07

    Добавлю к ответу endymion:
    Как правило, клиентское приложение точно знает базовый адрес, и, соотвественно, получает ресурс (путь) относительно него. Тогда же, когда путь определен не относительно базового адреса, а «за пределами домена адресов» использование прямой ссылки более оправдано, чем прикручивание HATEOAS к велосипеду.


  1. Losted
    10.01.2020 20:50

    Вот только все эти ссылки работают хорошо только для сферической ситуации в вакууме. Обычно бизнес-процесс реализуется в формате «возьми вон ту штуку, затем если в ней что-то равно чему-то, то сделай что-то еще с другой штукой и, если оно ответило условным конфликтом — выплюнь сообщение в очередь». Потенциально это все, конечно, можно развесить на линки, но реально в 90% случаях я вижу код формата ServiceAClient.get(id) и ServiceBClient.doStuff(id, blahBlah), который вызывается из какого-нибудь сервиса в приложении. В таком случае все эти ссылки становятся бессмысленными.

    Формально можно спорить, что можно было бы в Сервис А добавить ссылку на операцию из Service B, но это тот-же самый хардкод урлов, но в другом месте. Лучше уж в service discovery вложиться, как мне кажется