Глава 02. Компоненты гипермедиа-системы


Гипермедийная система состоит из ряда компонентов, в том числе:

  • Гипермедиа, такие как HTML.

  • Сетевой протокол, например HTTP.

  • Сервер, предоставляющий API гипермедиа, отвечающий на сетевые запросы ответами гипермедиа.

  • Клиент, который правильно интерпретирует эти ответы.

В этой главе мы рассмотрим эти компоненты и их реализацию в контексте Интернета.

После того, как мы рассмотрели основные компоненты Интернета как гипермедийной системы, мы рассмотрим некоторые ключевые идеи, лежащие в основе этой системы, особенно те, которые были разработаны Роем Филдингом в его диссертации «Архитектурные стили и проектирование сетевых программных архитектур». Мы увидим, откуда взялись термины REpresentational State Transfer (REST), RESTful и Hypermedia As The Engine Of Application State (HATEOAS), и проанализируем эти термины в контексте Интернета.

Это должно дать вам более глубокое понимание теоретической основы Интернета как гипермедийной системы, того, как они должны сочетаться друг с другом и почему приложения, управляемые гипермедиа, являются RESTful, тогда как API-интерфейсы JSON — несмотря на то, как термин REST в настоящее время используется в промышленность — нет.

Компоненты гипермедиа-системы

Гипермедиа

Фундаментальная технология системы гипермедиа — это гипермедиа, которая позволяет клиенту и серверу взаимодействовать друг с другом динамическим, нелинейным образом. Опять же, что делает гипермедиа гипермедиа, так это наличие элементов управления гипермедиа : элементов, которые позволяют пользователям выбирать нелинейные действия внутри гипермедиа. Пользователи могут взаимодействовать со средствами массовой информации не только путем простого чтения от начала до конца.

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

В случае HTML эти ссылки и формы обычно указывают цель своих операций с помощью унифицированных указателей ресурсов (URL) :

Единый указатель ресурсов

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

URL-адрес — это строка, состоящая из различных подкомпонентов:

[схема]://[информация пользователя]@[хост]:[порт][путь]?[запрос]#[фрагмент]

Многие из этих подкомпонентов не являются обязательными и часто опускаются.

Типичный URL-адрес может выглядеть так:

https://hypermedia.systems/book/contents/

Этот конкретный URL-адрес состоит из следующих компонентов:

  • Протокол или схема (в данном случае https)

  • Домен (например, hypermedia.systems)

  • Путь (например, /book/contents)

Этот URL-адрес уникальным образом идентифицирует извлекаемый ресурс в Интернете, к которому может быть отправлен HTTP-запрос гипермедийным клиентом, «говорящим» по HTTPS, например веб-браузером. Если этот URL-адрес обнаружен как ссылка на элемент управления гипермедиа в документе HTML, это означает, что на другой стороне сети есть сервер гипермедиа , который также понимает HTTPS и который может ответить на этот запрос представлением данный ресурс (или перенаправить вас в другое место и т. д.)

Обратите внимание, что URL-адреса часто не полностью записываются в HTML. Очень часто можно увидеть теги привязки, которые выглядят следующим образом, например:

<a href="/book/contents/">Table Of Contents</a>

Здесь у нас есть относительная ссылка на гипермедиа, где протокол, хост и порт подразумеваются как протокол, хост и порт «текущего документа», то есть такие же, какие бы протокол и сервер ни использовались для получения текущей HTML-страницы. Таким образом, если эта ссылка была найдена в HTML-документе, полученном из https://hypermedia.systems/, то подразумеваемый URL-адрес этой привязки будет https://hypermedia.systems/book/contents/.

Протоколы гипермедиа

Элемент управления гипермедиа (ссылка) выше сообщает браузеру: «Когда пользователь нажимает на этот текст, выдайте запрос на https://hypermedia.systems/book/contents/использование протокола передачи гипертекста» или HTTP.

HTTP — это протокол , используемый для передачи HTML (гипермедиа) между браузерами (клиентами гипермедиа) и серверами (серверами гипермедиа) и, как таковой, является ключевой сетевой технологией, которая объединяет распределенную систему гипермедиа в Интернете.

HTTP версии 1.1 — относительно простой сетевой протокол, поэтому давайте посмотрим, как GETбудет выглядеть запрос, инициируемый тегом привязки. Это запрос, который по умолчанию будет отправлен на сервер hypermedia.systems, расположенный по адресу 80 :

GET /book/contents/ HTTP/1.1
Accept: text/html,*/*
Host: hypermedia.systems

Первая строка указывает, что это HTTP- GETзапрос. Затем он указывает путь к запрашиваемому ресурсу. Наконец, он содержит версию HTTP для этого запроса.

После этого идет серия заголовков HTTP-запроса : отдельные строки пар имя/значение, разделенные двоеточием. Заголовки запросов предоставляют метаданные , которые могут использоваться сервером, чтобы точно определить, как ответить на запрос клиента. В этом случае с помощью Acceptзаголовка браузер сообщает, что предпочитает HTML в качестве формата ответа, но примет любой ответ сервера.

Далее у него есть Hostзаголовок, который указывает, на какой сервер был отправлен запрос. Это полезно, когда на одном хосте размещено несколько доменов.

HTTP-ответ сервера на этот запрос может выглядеть примерно так:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 870
Server: Werkzeug/2.0.2 Python/3.8.10
Date: Sat, 23 Apr 2022 18:27:55 GMT

<html lang="en">
<body>
  <header>
    <h1>HYPERMEDIA SYSTEMS</h1>
  </header>
  ...
</body>
</html>

В первой строке ответа HTTP указывается используемая версия HTTP, за которой следует код ответа200 , указывающий, что данный ресурс был найден и что запрос выполнен успешно. За ним следует строка, OKсоответствующая коду ответа. (Фактическая строка не имеет значения, это код ответа, который сообщает клиенту результат запроса, о чем мы более подробно поговорим ниже.)

После первой строки ответа, как и в случае с HTTP-запросом, мы видим серию заголовков ответа , которые предоставляют клиенту метаданные, помогающие правильно отобразить представление ресурса.

Наконец, мы видим новый HTML-контент. Это содержимое представляет собой HTML- представление запрошенного ресурса, в данном случае оглавление книги. Браузер будет использовать этот HTML-код для замены всего содержимого в своем окне отображения, показывая пользователю эту новую страницу и обновляя адресную строку, чтобы отразить новый URL-адрес.

HTTP-методы

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

В HTTP доступно множество методов; Наиболее практическое значение для разработчиков имеют следующие:

GET

Запрос GET извлекает представление указанного ресурса. Запросы GET не должны изменять данные.

POST

Запрос POST отправляет данные в указанный ресурс. Это часто приводит к изменению состояния на сервере.

PUT

Запрос PUT заменяет данные указанного ресурса. Это приводит к изменению состояния на сервере.

PATCH

Запрос PATCH заменяет данные указанного ресурса. Это приводит к изменению состояния на сервере.

DELETE

Запрос DELETE удаляет указанный ресурс. Это приводит к изменению состояния на сервере.

Эти методы примерно соответствуют шаблону «Создать/Чтение/Обновление/Удалить» или CRUD, встречающемуся во многих приложениях:

  • POSTсоответствует созданию ресурса.

  • GETсоответствует Чтению ресурса.

  • PUTи PATCHпереписываемся с Обновлением ресурса.

  • DELETEсоответствует, ну, удалению ресурса.

Ставка против публикации

Хотя действия HTTP примерно соответствуют CRUD, они не совпадают. В технических спецификациях этих методов такой связи нет, и их часто трудно читать. Вот, например, документация о различии a POSTи a PUTиз RFC-2616 .

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

— RFC-2616, https://www.rfc-editor.org/rfc/rfc2616#section-9.6 .

Проще говоря, a POSTможет обрабатываться сервером практически так, как ему нравится, тогда как a PUTдолжен обрабатываться как «замена» ресурса, хотя язык снова позволяет серверу делать практически все, что он пожелает, в рамках ограничение идемпотентности .

В правильно структурированной системе гипермедиа на основе HTML вы должны использовать соответствующий метод HTTP для операции, которую выполняет конкретный элемент управления гипермедиа. Например, если элемент управления гипермедиа, такой как кнопка, удаляет ресурс, в идеале DELETEдля этого он должен выдать HTTP-запрос.

Однако странная особенность HTML заключается в том, что встроенные элементы управления гипермедиа могут выдавать только HTTP GETи POSTзапросы.

Теги привязки всегда выдают GETзапрос.

Формы могут выдавать либо атрибут GET, либо POSTиспользовать его method.

PUTНесмотря на то, что HTML — самый популярный в мире гипермедиа — был разработан вместе с HTTP (который, в конце концов , является протоколом передачи гипертекста!): если вы хотите отправлять запросы или, вам в настоящее время приходится прибегать для PATCHэтого DELETEк JavaScript . Поскольку a POSTможет делать практически все, в конечном итоге он используется для любой мутации на сервере и PUTостается в стороне в простых приложениях на основе HTML PATCH.DELETE

Это очевидный недостаток HTML как гипермедиа; было бы замечательно увидеть это исправленным в спецификации HTML. А пока, в главе 4, мы обсудим способы обойти эту проблему.

Коды ответа HTTP

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

Вероятно, наиболее знакомый код ответа веб-разработчикам — 404, что означает «Не найден». Это код ответа, который возвращается веб-серверами, когда у них запрашивается несуществующий ресурс.

HTTP разбивает коды ответов на различные категории:

100-199

Информационные ответы, предоставляющие информацию о том, как сервер обрабатывает ответ.

200-299

Успешные ответы, указывающие на то, что запрос выполнен успешно.

300-399

Ответы на перенаправление, указывающие, что запрос следует отправить на другой URL-адрес.

400-499

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

500-599

Ответы сервера об ошибках, указывающие на то, что сервер обнаружил внутреннюю ошибку при попытке ответить на запрос.

В каждой из этих категорий имеется несколько кодов ответов для конкретных ситуаций.

Вот некоторые из наиболее распространенных и интересных из них:

200 OK

HTTP-запрос выполнен успешно.

301 Moved Permanently

URL-адрес запрошенного ресурса навсегда перемещен в новое место, и новый URL-адрес будет указан в Locationзаголовке ответа.

302 Found

URL-адрес запрошенного ресурса временно перемещен в новое место, и новый URL-адрес будет указан в Locationзаголовке ответа.

303 See Other

URL-адрес запрошенного ресурса перемещен в новое место, и новый URL-адрес будет указан в Locationзаголовке ответа. Кроме того, этот новый URL-адрес следует получить с помощью GETзапроса.

401 Unauthorized

Клиент еще не аутентифицирован (да, аутентифицирован, несмотря на имя) и должен пройти аутентификацию для получения данного ресурса.

403 Forbidden

У клиента нет доступа к этому ресурсу.

404 Not Found

Сервер не может найти запрошенный ресурс.

500 Internal Server Error

Сервер обнаружил ошибку при попытке обработать ответ.

Между кодами ответов HTTP есть некоторые довольно тонкие различия (и, честно говоря, между ними есть некоторая двусмысленность). Например, разница между перенаправлением 302и перенаправлением заключается в том, что первое будет отправлять запрос на новый URL-адрес, используя тот же метод HTTP, что и первоначальный запрос, тогда как второе всегда будет использовать расширение . Это небольшое, но часто решающее различие, как мы увидим далее в книге.303GET

Хорошо созданное приложение, управляемое гипермедиа, будет использовать преимущества как методов HTTP, так и кодов ответов HTTP для создания разумного API гипермедиа. Например, вы не хотите создавать приложение, управляемое гипермедиа, которое использует POSTметод для всех запросов и отвечает на каждый ответ. 200 OK(Некоторые API данных JSON, созданные на основе HTTP, делают именно это!)

Вместо этого при создании приложения, управляемого гипермедиа, вы хотите следовать «зерну» Интернета и использовать методы HTTP и коды ответов так, как они были разработаны для использования.

Кэширование HTTP-ответов

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

Поведение кэша HTTP-ответа от сервера можно указать с помощью Cache-Controlзаголовка ответа. Этот заголовок может иметь несколько различных значений, указывающих на возможность кэширования данного ответа. Если, например, заголовок содержит значение max-age=60, это означает, что клиент может кэшировать этот ответ в течение 60 секунд и ему не нужно выдавать еще один HTTP-запрос для этого ресурса, пока этот лимит времени не истечет.

Еще один важный заголовок ответа, связанный с кэшированием, — Vary. Этот заголовок ответа можно использовать, чтобы указать, какие именно заголовки в HTTP-запросе образуют уникальный идентификатор для кэшированного результата. Это становится важным, чтобы позволить браузеру правильно кэшировать контент в ситуациях, когда конкретный заголовок влияет на форму ответа сервера.

Например, в приложениях на основе htmx распространенным шаблоном является использование пользовательского заголовка, установленного htmx, HX-Requestчтобы различать «обычные» веб-запросы и запросы, отправленные htmx. Чтобы правильно кэшировать ответ на эти запросы, HX-Requestзаголовок запроса должен быть указан заголовком Varyответа.

Полное обсуждение кэширования HTTP-ответов выходит за рамки этой главы; см. статью MDN о HTTP-кешировании, если вы хотите узнать больше по этой теме.

Гипермедиа-серверы

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

Это оказывается одним из лучших аспектов использования гипермедиа в качестве основной технологии создания веб-приложения: это устраняет необходимость использования JavaScript в качестве серверной технологии. Если вы используете интерфейс на основе одностраничного приложения с большим количеством JavaScript и API данных JSON, вы также почувствуете значительное давление при развертывании JavaScript на внутренней стороне.

В последней ситуации у вас уже есть тонна кода, написанного на JavaScript. Зачем поддерживать две отдельные базы кода на двух разных языках? Почему бы не создать многократно используемую логику домена как на стороне клиента, так и на стороне сервера? Теперь, когда в JavaScript доступны отличные серверные технологии, такие как Node и Deno, почему бы просто не использовать для всего один язык?

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

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

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

Если вы занимаетесь работой с искусственным интеллектом, возможно, вы захотите использовать Lisp, опираясь на язык с долгой историей в этой области исследований.

Возможно, вы энтузиаст функционального программирования и хотите использовать OCaml или Haskell. Возможно, вам просто очень нравится Джулия или Ним.

Это вполне веские причины для выбора конкретной серверной технологии!

Используя гипермедиа в качестве системной архитектуры, вы можете выбрать любой из этих вариантов. На клиентской стороне просто нет большой базы кода JavaScript, которая заставляет вас использовать JavaScript на внутренней стороне.

Гипермедиа о чем угодно (ВОЙ)

В htmx-сообществе мы называем это (иронично) стеком HOWL: Гипермедиа на все, что захотите. Сообщество htmx мультиязычное и мультифреймворчное, в нем есть как рубисты, так и питонисты, лисперы и хаскеллеры. Есть даже энтузиасты JavaScript! Все эти языки и платформы способны использовать гипермедиа и по-прежнему могут совместно использовать методы и предлагать поддержку друг другу, поскольку они имеют общую базовую архитектуру: все они используют Интернет как систему гипермедиа.

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

Гипермедийные клиенты

Теперь мы подошли к последнему основному компоненту гипермедийной системы: гипермедийному клиенту. Клиенты гипермедиа — это программное обеспечение, которое понимает, как правильно интерпретировать конкретную гипермедиа и элементы управления гипермедиа внутри нее. Каноническим примером, конечно же, является веб-браузер, который понимает HTML и может предоставить его пользователю для взаимодействия. Веб-браузеры — это невероятно сложные программы. (На самом деле они настолько сложны, что их часто перепрофилируют из гипермедийных клиентов в своего рода кроссплатформенную виртуальную машину для запуска одностраничных приложений.)

Однако браузеры — не единственные клиенты гипермедиа. В последнем разделе этой книги мы рассмотрим Hyperview, гипермедиа, ориентированную на мобильные устройства. Одной из выдающихся особенностей Hyperview является то, что он не просто предоставляет гипермедиа HXML, но также предоставляет работающий гипермедиа-клиент для этого гипермедиа. Это делает создание правильного приложения, управляемого гипермедиа, с помощью Hyperview чрезвычайно простым.

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

Это одна из причин, почему API-интерфейсы JSON редко успешно применяют элементы управления гипермедиа: API-интерфейсы JSON обычно используются кодом, который ожидает фиксированного формата и не предназначен для использования в качестве клиента гипермедиа. Это вполне понятно: создать хороший гипермедиа-клиент сложно! Для таких клиентов JSON API возможности элементов управления гипермедиа, встроенных в ответ API, не имеют значения и часто просто раздражают:

Короткий ответ на этот вопрос: HATEOAS не подходит для большинства современных вариантов использования API. Именно поэтому спустя почти 20 лет HATEOAS до сих пор не получил широкого распространения среди разработчиков. GraphQL, с другой стороны, распространяется со скоростью лесного пожара, поскольку решает реальные проблемы.

— Фредди Карлбом, https://techblog.commercetools.com/graphql-and-rest-level-3-hateoas-70904ff1f9cf

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

ОТДЫХ

Теперь, когда мы рассмотрели основные компоненты гипермедиа-системы, пришло время более глубоко изучить концепцию REST. Термин «REST» взят из докторской диссертации Роя Филдинга по архитектуре Интернета. Филдинг написал свою диссертацию в Калифорнийском университете в Ирвине после того, как помог создать большую часть инфраструктуры ранней сети, включая веб-сервер Apache. Рой пытался формализовать и описать новую распределенную вычислительную систему, которую он помог построить.

Мы собираемся сосредоточиться на том, что, по нашему мнению, является наиболее важным разделом работы Филдинга с точки зрения веб-разработки: раздел 5.1. В этом разделе содержатся основные концепции (Филдинг называет их ограничениями ) передачи репрезентативного состояния, или REST.

Однако прежде чем мы углубимся в суть, важно понять, что Филдинг обсуждает REST как сетевую архитектуру , то есть совершенно другой способ проектирования распределенной системы. И, кроме того, как новая сетевая архитектура, которую следует противопоставлять более ранним подходам к распределенным системам.

Также важно подчеркнуть, что на момент написания Филдингом своей диссертации API JSON и AJAX еще не существовало. Он описывал раннюю сеть, в которой HTML передавался через HTTP ранними браузерами, как систему гипермедиа.

Сегодня, по странному стечению обстоятельств, термин «REST» в основном ассоциируется с API-интерфейсами данных JSON, а не с HTML и гипермедиа. Это становится чрезвычайно забавно, если осознать, что подавляющее большинство API-интерфейсов данных JSON не являются RESTful в исходном смысле и фактически не могут быть RESTful, поскольку они не используют естественный формат гипермедиа.

Еще раз подчеркнем: REST, как его придумал Филдинг, описывает сеть до API , и отказ от текущего, общепринятого использования термина REST, который просто означает «JSON API», необходим для правильного понимания этой идеи. .

«Ограничения» REST

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

Вот ограничения контуров REST Fielding:

  • Это клиент-серверная архитектура (раздел 5.1.2).

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

  • Он должен позволять кэширование (раздел 5.1.4).

  • Он должен иметь единый интерфейс (раздел 5.1.5).

  • Это многоуровневая система (раздел 5.1.6).

  • При желании он может разрешить использование кода по требованию (раздел 5.1.7), то есть создание сценариев.

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

Ограничение клиент-сервер

См. раздел 5.1.2 для ограничения клиент-сервер.

Модель REST, которую описывал Филдинг, включала в себя как клиентов (браузеров, в случае Интернета), так и серверов (таких как веб-сервер Apache, над которым он работал), взаимодействующих через сетевое соединение. Таков был контекст его работы: он описывал сетевую архитектуру Всемирной паутины и противопоставлял ее более ранним архитектурам, в частности сетевым моделям с толстым клиентом, таким как архитектура брокера общих объектных запросов (CORBA).

Должно быть очевидно, что любое веб-приложение, независимо от того, как оно спроектировано, будет удовлетворять этому требованию.

Ограничение безгражданства

См. раздел 5.1.3 для ограничения Stateless.

По описанию Филдинга, система RESTful не имеет состояния: каждый запрос должен инкапсулировать всю информацию, необходимую для ответа на этот запрос, без сохранения какого-либо побочного состояния или контекста ни на клиенте, ни на сервере.

На практике сегодня для многих веб-приложений мы фактически нарушаем это ограничение: обычно устанавливается файл cookie сеанса , который действует как уникальный идентификатор для данного пользователя и отправляется вместе с каждым запросом. Хотя этот файл cookie сеанса сам по себе не сохраняет состояние (он отправляется с каждым запросом), он обычно используется в качестве ключа для поиска информации, хранящейся на сервере, в том, что обычно называют «сеансом».

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

Это нарушение архитектурного ограничения REST без гражданства оказалось полезным для создания веб-приложений и, похоже, не оказало серьезного влияния на общую гибкость Интернета. Но стоит иметь в виду, что даже приложения Web 1.0 часто нарушают чистоту REST ради прагматических компромиссов.

И надо сказать, что сеансы действительно вызывают дополнительные трудности при развертывании гипермедиа-серверов; им может потребоваться общий доступ к информации о состоянии сеанса, хранящейся во всем кластере. Таким образом, Филдинг был прав, отметив, что идеальная система RESTful, не нарушающая это ограничение, будет проще и, следовательно, более надежной.

Ограничение кэширования

См. раздел 5.1.4 для получения информации об ограничении кэширования.

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

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

Единообразное ограничение интерфейса

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

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

См. раздел 5.1.5 для ограничения Uniform Interface.

В этом разделе Филдинг говорит:

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

— Рой Филдинг, Архитектурные стили и проектирование сетевых архитектур программного обеспечения.

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

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

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

Что сегодня может быть более интересным, так это понятие ресурса , которое идентифицируется таким образом: в системе RESTful любой тип данных, на которые можно ссылаться, то есть цель ссылки на гипермедиа, считается ресурсом. URL-адреса, хотя и достаточно распространены сегодня, в конечном итоге решают очень сложную проблему уникальной идентификации любого ресурса в Интернете.

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

В системе RESTful представления ресурса передаются между клиентами и серверами. Эти представления могут содержать как данные, так и метаданные о запросе (например, «управляющие данные», такие как метод HTTP или код ответа). Конкретный формат данных или тип носителя может использоваться для представления данного ресурса клиенту, и этот тип носителя может быть согласован между клиентом и сервером.

Последний аспект единого интерфейса мы видели в заголовке Acceptприведенных выше запросов.

Самоописательные сообщения

Ограничение самоописательных сообщений в сочетании со следующим, HATEOAS, образует то, что мы считаем ядром единого интерфейса REST и почему гипермедиа обеспечивает такую ​​мощную системную архитектуру.

Ограничение самоописательных сообщений требует, чтобы в системе RESTful сообщения содержали самоописание .

Это означает, что в ответе должна присутствовать вся информация, необходимая как для отображения , так и для работы с представляемыми данными. В правильно RESTful-системе не может быть никакой дополнительной «побочной» информации, необходимой клиенту для преобразования ответа сервера в полезный пользовательский интерфейс. Все должно «находиться» в самом сообщении, в виде элементов управления гипермедиа.

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

Рассмотрим два разных потенциальных ответа HTTP-сервера на URL-адрес https://example.com/contacts/42.

Оба ответа вернут информацию о контакте, но каждый ответ будет принимать совершенно разные формы.

Первая реализация возвращает HTML-представление:

<html lang="en">
<body>
<h1>Joe Smith</h1>
<div>
    <div>Email: [email protected]</div>
    <div>Status: Active</div>
</div>
<p>
    <a href="/contacts/42/archive">Archive</a>
</p>
</body>
</html>

Вторая реализация возвращает представление JSON:

{
  "name": "Joe Smith",
  "email": "[email protected]",
  "status": "Active"
}

Что мы можем сказать о различиях между этими двумя ответами?

Одна вещь, которая может сразу бросаться в глаза, это то, что представление JSON меньше, чем представление HTML. Филдинг отмечает именно этот компромисс при использовании архитектуры RESTful:

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

— Рой Филдинг, Архитектурные стили и проектирование сетевых архитектур программного обеспечения.

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

Чтобы понять эти другие цели, сначала обратите внимание, что в HTML-представлении есть гиперссылка для перехода на страницу для архивирования контакта. Представление JSON, напротив, не имеет этой ссылки.

Каковы последствия этого факта для клиента JSON API?

Это означает, что клиент JSON API должен заранее точно знать, какие другие URL-адреса (и методы запроса) доступны для работы с контактной информацией. Если клиент JSON может каким-либо образом обновить этот контакт, он должен знать, как это сделать, из какого-либо источника информации, внешнего по отношению к сообщению JSON. Если у контакта другой статус, скажем «В архиве», меняет ли это допустимые действия? Если да, то каковы новые допустимые действия?

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

Сравните это с ответом гипермедиа (HTML). В этом случае гипермедийному клиенту (то есть браузеру) нужно только знать, как визуализировать данный HTML. Ему не нужно понимать, какие действия доступны для этого контакта: они просто закодированы в самом ответе HTML как элементы управления гипермедиа. Ему не нужно понимать, что означает поле статуса. На самом деле клиент даже не знает, что такое контакт!

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

Эта разница между двумя ответами демонстрирует суть REST и гипермедиа, что делает их такими мощными и гибкими: клиентам (опять же, веб-браузерам) не нужно ничего понимать о представляемых базовых ресурсах.

Только браузерам (только! Как будто это легко!) необходимо понимать, как интерпретировать и отображать гипермедиа, в данном случае HTML. Это дает системам на основе гипермедиа беспрецедентную гибкость в работе с изменениями как в поддерживающих представлениях, так и в самой системе.

Гипермедиа как двигатель состояния приложения (HATEOAS)

Последнее дополнительное ограничение универсального интерфейса заключается в том, что в системе RESTful гипермедиа должна быть «двигателем состояния приложения». Иногда это сокращают как «HATEOAS», хотя Филдинг при обсуждении этого вопроса предпочитает использовать терминологию «ограничение гипермедиа».

Это ограничение тесно связано с предыдущим ограничением сообщения с самоописанием. Давайте еще раз рассмотрим две разные реализации конечной точки /contacts/42: одна возвращает HTML, а другая — JSON. Давайте обновим ситуацию так, чтобы контакт, указанный по этому URL-адресу, был заархивирован.

Как выглядят наши ответы?

Первая реализация возвращает следующий HTML-код:

<html lang="en">
<body>
<h1>Joe Smith</h1>
<div>
    <div>Email: [email protected]</div>
    <div>Status: Archived</div>
</div>
<p>
    <a href="/contacts/42/unarchive">Unarchive</a>
</p>
</body>
</html>

Вторая реализация возвращает следующее представление JSON:

{
  "name": "Joe Smith",
  "email": "[email protected]",
  "status": "Archived"
}

Здесь важно отметить, что, поскольку HTML-ответ представляет собой самоописывающее сообщение, теперь показывает, что операция «Архивировать» больше недоступна, и стала доступна новая операция «Разархивировать». HTML-представление контакта кодирует состояние приложения; он точно кодирует то, что можно и что нельзя сделать с этим конкретным представлением, чего не делает представление JSON.

Клиент, интерпретирующий ответ JSON, опять же должен понимать не только общую концепцию Контакта, но и конкретно, что означает поле «статус» со значением «В архиве». Он должен точно знать, какие операции доступны для «заархивированного» контакта, чтобы правильно отображать их конечному пользователю. Состояние приложения не кодируется в ответе, а скорее передается через сочетание необработанных данных и информации по побочным каналам, например документации API.

Более того, сегодня в большинстве интерфейсных SPA-инфраструктур эта контактная информация будет храниться в памяти в объекте JavaScript, представляющем модель контакта, а данные страницы хранятся в объектной модели документа (DOM) браузера. DOM будет обновляться на основе изменений в этой модели, то есть DOM будет «реагировать» на изменения в этой поддерживающей модели JavaScript.

Этот подход, конечно, не использует гипермедиа в качестве механизма состояния приложения: скорее, он использует модель JavaScript в качестве механизма состояния приложения и синхронизирует эту модель с сервером и браузером.

При подходе HTML гипермедиа действительно является механизмом состояния приложения: на стороне клиента нет дополнительной модели, и все состояние выражается непосредственно в гипермедиа, в данном случае в HTML. По мере изменения состояния на сервере это отражается в представлении (то есть HTML), отправляемом обратно клиенту. Гипермедийный клиент (браузер) ничего не знает о контактах, что такое понятие «Архивирование» или что-либо еще о конкретной модели домена для этого ответа: он просто знает, как отображать HTML.

Поскольку гипермедиа-клиенту не нужно ничего знать о модели сервера, кроме того, как отображать гипермедиа клиенту, он невероятно гибок в отношении представлений, которые он получает и отображает пользователям.

HATEOAS и отток API

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

Как это изменит каждый из двух ответов — HTML и JSON — от сервера?

HTML-представление теперь может выглядеть так:

<html lang="en">
<body>
<h1>Joe Smith</h1>
<div>
    <div>Email: [email protected]</div>
    <div>Status: Active</div>
</div>
<p>
    <a href="/contacts/42/archive">Archive</a>
    <a href="/contacts/42/message">Message</a>
</p>
</body>
</html>

С другой стороны, представление JSON может выглядеть так:

{
  "name": "Joe Smith",
  "email": "[email protected]",
  "status": "Active"
}

Обратите внимание, что представление JSON не изменилось. Никаких указаний на эту новую функциональность нет. Вместо этого клиент должен знать об этом изменении, предположительно, через некоторую общую документацию между клиентом и сервером.

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

Довольно хитрый трюк!

Теперь, в этом случае, если клиент JSON не обновлен должным образом, состояние ошибки относительно безопасно: новая часть функциональности просто недоступна для пользователей. Но рассмотрим более серьезное изменение API: что, если функциональность архивирования будет удалена? Или что, если URL-адреса или методы HTTP для этих операций каким-либо образом изменились?

В этом случае клиент JSON может быть поврежден гораздо более серьезно.

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

Из этого вытекает важный факт: благодаря такой гибкости API-интерфейсы гипермедиа не испытывают головной боли с управлением версиями, как API-интерфейсы данных JSON .

После того, как приложение, управляемое гипермедиа, «введено» (то есть загружено через некоторый URL-адрес точки входа), все функциональные возможности и ресурсы отображаются через самоописывающие сообщения. Поэтому нет необходимости обмениваться документацией с клиентом: клиент просто рендерит гипермедиа (в данном случае HTML) и все работает. Когда происходит изменение, нет необходимости создавать новую версию API: клиенты просто извлекают обновленную гипермедиа, которая кодирует в ней новые операции и ресурсы, и отображают ее пользователям для работы.

Многоуровневая система

Последнее «обязательное» ограничение для системы RESTful, которое мы рассмотрим, — это ограничение многоуровневой системы. Это ограничение можно найти в разделе 5.1.6 диссертации Филдинга.

Честно говоря, после ажиотажа, связанного с ограничением единообразного интерфейса, ограничение «многоуровневой системы» немного разочаровывает. Но это все же стоит понять, и на самом деле оно эффективно используется в Интернете. Ограничение требует, чтобы архитектура RESTful была «многоуровневой», позволяющей нескольким серверам выступать в качестве посредников между клиентом и возможным сервером «источника истины».

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

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

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

Не так интересно для разработчиков веб-приложений, как единый интерфейс, по крайней мере, на наш взгляд, но, тем не менее, полезно.

Необязательное ограничение: код по требованию

Мы назвали ограничение «Многоуровневая система» последним «обязательным» ограничением, поскольку Филдинг упоминает одно дополнительное ограничение для системы RESTful. Это ограничение Code On Demand несколько неловко описано как «необязательное» (раздел 5.1.7).

В этом разделе Филдинг говорит:

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

— Рой Филдинг, Архитектурные стили и проектирование сетевых архитектур программного обеспечения.

Таким образом, сценарии были и остаются встроенным аспектом исходной RESTful-модели Интернета, и поэтому их, конечно, следует разрешить в приложениях, управляемых гипермедиа.

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

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

Заключение

После этого глубокого погружения в компоненты и концепции, лежащие в основе гипермедиа-систем, включая идеи Роя Филдинга об их работе, мы надеемся, что вы гораздо лучше поймете REST и, в частности, единый интерфейс и HATEOAS. Мы надеемся, что вы понимаете , почему эти характеристики делают гипермедийные системы такими гибкими.

Если вы до сих пор не осознавали всю значимость REST и HATEOAS, не расстраивайтесь: некоторым из нас потребовалось более десяти лет работы в области веб-разработки и создания библиотеки, ориентированной на гипермедиа, чтобы понять особенности природа HTML, гипермедиа и Интернета!

HTML-примечания: HTML5-суп

Начало мудрости – называть вещи своими именами.

— Конфуций

Такие элементы, как <section>, <article>, <nav>, <header>, <footer>, <figure>стали своего рода сокращением HTML.

Используя эти элементы, страница может давать ложные обещания, например, что <article>элементы являются автономными, многократно используемыми объектами, клиентам, таким как браузеры, поисковые системы и парсеры, которые не могут знать лучше. Чтобы избежать этого:

  • Убедитесь, что используемый вами элемент соответствует вашему варианту использования. Проверьте спецификацию HTML.

  • Не пытайтесь быть конкретными, когда вы не можете или не нуждаетесь в этом. Иногда <div>это нормально.

Наиболее авторитетным ресурсом для изучения HTML является спецификация HTML. Текущая спецификация находится на https://html.spec.whatwg.org/multipage . [ 1 ] Не нужно полагаться на слухи, чтобы идти в ногу с развитием HTML.

В разделе 4 спецификации представлен список всех доступных элементов, включая то, что они представляют, где они могут встречаться и что им разрешено содержать. Он даже сообщает вам, когда можно опускать закрывающие теги!


  1. Одностраничная версия слишком медленно загружается и отображается на большинстве компьютеров. Существует также версия для разработчиков в /dev, но стандартная версия имеет более приятный стиль.

Last updated