Как стать автором
Обновить

Комментарии 26

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


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

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

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

Тема очень обширна и на самом деле заслуживает отдельной статьи, но я по мере своих сил перескажу суть. 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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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

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


«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


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

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

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

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

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

Хотелось бы увидеть реальное веб-приложение (хотя бы демо уровня чуть выше TodoMVC), полностью основанное на HATEOAS.

Примеры использования HATEOAS и упоминания реальных веб-приложений есть в презентации:
Spring HATEOAS: Hypermedia APIs with Spring
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории