Статьи

Почему HATEOAS не ведьма, чтобы сжечь

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

HATEOAS для чайников

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

Например, вы переходите на эту страницу по некоторому URL-адресу на css.dzone.com после того, как нажали на ссылку в Twitter или другую ссылку на странице DZone; может быть, даже один на точке входа css.dzone.com.
На уровне API HATEOAS подразумевает, что выходные данные ваших вызовов API содержат дополнительные ссылки на другие ресурсы; в зависимости от принятых вами форматов эти ссылки могут быть помещены в заголовки или в тела ответа JSON или Atom.

Если вы создадите все свои API, следуя этому практическому правилу, это будет означать, что ресурс / posts будет содержать ссылки на все / post / 1 , / post / 2 . Для вил это бесполезная абстракция!

Более серьезно: дизайнерские решения

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

Когда я впервые услышал о HATEOAS и прочитал книгу Майка Амундсена « Создание API гипермедиа»Я подумал: это движение пытается перестроить веб-приложения, в которых в ответ встраиваются ссылки на следующие ресурсы, но с машинами на стороне клиента, а не людьми. Я никогда не видел интеллектуальных машин, способных ориентироваться в браузере, так что это обречено на провал.

Затем я начал работать с приложениями со сложными рабочими процессами, где ваше взаимодействие не останавливается на POST и GET для получения результата, а продолжается для 4, 5 или более различных запросов; необходимость поддержки более одного типа клиентов, включая развернутые устройства Android. И я был рад узнать, что включение URL в ответы разрешено .

Распределение обязанностей

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

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

Например, недавно я работал над точкой входа API, которую мы будем называть / widgets. При выполнении POST для / widgets пользователь должен перейти на страницу вновь созданного виджета, и он отправляется туда с заголовком Location, содержащим полный URL:

HTTP/1.1 201 Accepted
Location: http://www.example.com/widgets/42

Это дизайн HATEOAS: вы не полагаетесь на то, что знаете больше URL, чем начальный URL для навигации. В этом случае преимущество очевидно, так как клиент не знает: id в / widgets /: id, который он должен загрузить.

Чтобы решить эту единственную проблему, вы можете передать новый: id в теле ответа в некотором приемлемом формате:

{"id": 42, ...}

Так почему же клиент должен следовать Location вместо составления своего собственного URL? Прежде всего, первый выбор для клиента еще менее эффективен: именно сервер должен создать URL-адрес для возврата, а не просто сбросить ресурс. Однако в действительности происходит разделение обязанностей между клиентом и сервером, чтобы клиент знал основной шаг рабочего процесса (создайте виджет, загрузите его, выполните с ним некоторые другие действия), а сервер управляет фактическим расположением ответ. Таким образом, дизайн HATEOAS позволяет вам, владельцу сервера, изменить некоторые интересные вещи в будущем:

  • сервер может решить не отправлять пользователя напрямую в / widgets /: id, но выполнить промежуточное перенаправление . Например, некоторые операторы требуют этого промежуточного перенаправления на одну из своих страниц, чтобы разрешить идентификацию пользователя. Если клиент правильно следует заголовкам Location, вы можете выполнить любое количество переадресаций между POST on / widgets и следующим шагом / widget /: id.
  • сервер может решить отправить пользователя на другой хост или приложение в целом. Во время перехода от устаревшей системы A к новой и свежей системе B может случиться так, что все новые виджеты, созданные на A, после этого будут перенаправлены на B, чтобы постепенно выводить часть A из строя, а не в результате большого взрыва .
  • Можно соответствующим образом изменить внутренние URL-адреса , например, поместив виджеты в / user /: name / widgets /: id, где: name — это пользователь, выполнивший POST. Это поддерживает изменение использования идентификаторов, которые являются уникальными для пользователя, а не глобально.

Я не говорю, что вам нужно оценить эти изменения в вашей системе или то, что они когда-нибудь произойдут с вами; просто этот дизайн на простом создании POST / поискового GET-запроса позволяет разместить их без изменения клиентов (которые могут быть развернуты на тысячах устройств Android и iOs).

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

Выводы

Если вам просто нужно выполнить операции CRUD, не создавайте объектно-ориентированное приложение, выберите реляционную базу данных и представьте ее через некоторые представления. Точно так же, если вам просто нужно выполнить операции CRUD с вашим API, не открывайте ссылки в ресурсах: придерживайтесь запросов POST и GET по фиксированным URL-адресам. То же самое происходит, если у вас есть полный контроль над всеми клиентами вашего API (в веб-приложениях, в отличие от мобильных приложений или других систем, которые должны интегрировать вас).

Но HATEOAS — это модель, которая позволяет вам распределять на сервере некоторые обязанности сценариев использования вашего API: каковы следующие этапы, где они выполняются, пропускать ли их или выполнять дальнейшие перенаправления. HATEOAS — это не напоминание WS- *, а наложение ярлыка REST на вызовы удаленных процедур — это напоминание о RMI и CORBA (сегодня слишком много аббревиатур!)