HATEOAS и формы, управляемые API

Я пытаюсь применить HATEOAS к существующему приложению, и у меня возникают проблемы с моделированием входных данных формы, которые будут управляться ответом API.

Приложение позволяет искать и бронировать соединения между двумя местами. Первая конечная точка позволяет искать соединения GET /connections?from = {lat,lon}&to = {lat,lon}&departure = {dateTime} и возвращает следующую полезную нагрузку (тело ответа).

[
  {
    "id": "aaa",
    "carrier": "Fast Bus",
    "price": 3.20,
    "departure": "2019-04-05T12:30"
  },
  {
    "id": "bbb",
    "carrier": "Airport Bus",
    "price": 4.60,
    "departure": "2019-04-05T13:30"
  },
  {
    "id": "ccc",
    "carrier": "Slow bus",
    "price": 1.60,
    "departure": "2019-04-05T11:30"
  }
]

Чтобы сделать заказ на одно из подключений, клиенту необходимо сделать запрос POST /orders с одним из следующих полезных нагрузок (тело запроса):

  • электронная почта требуется
    {
      "connectionId": "aaa",
      "email": "[email protected]"
    }
    
  • требуется электронная почта и номер рейса (перевозчик обрабатывает только соединения с аэропортом)
    {
      "connectionId": "bbb",
      "email": "[email protected]",
      "flightNumber": "EA1234"
    }
    
  • требуется номер телефона
    {
      "connectionId": "ccc",
      "phoneNumber": "+44 111 222 333"
    }
    

Полезная нагрузка различна, потому что разные соединения могут обрабатываться разными операторами связи, и каждому из них может требоваться определенный набор информации для предоставления. Я хотел бы сообщить клиенту API, какие поля обязательны при создании заказа. У меня вопрос, как мне это сделать с HATEOAS?

Я проверил разные спецификации и вот что я мог сказать, прочитав спецификации:

  1. ХАЛ и ГАЛ-ФОРМЫ Есть "_templates" но в самом шаблоне нет URI. Предполагается, что он работает с собственной ссылкой, которая в моем случае будет / соединения..., а не /заказы.
  2. JSON-LD Мне ничего не удалось найти о поддержке форм или шаблонов.
  3. JSON-API Мне ничего не удалось найти о поддержке форм или шаблонов.
  4. Коллекция+JSON В каждом документе есть не более одного "template", поэтому предполагается, что все элементы коллекции имеют одинаковые поля, что не так в моем приложении.
  5. Сирена Похоже, что "actions" подойдет для моего варианта использования, но проект кажется мертвым, и нет поддерживающих библиотек для многих основных языков.
  6. КПХЛ Проект кажется мертвым, очень мало документации и нет библиотек.
  7. Ион Есть хорошая поддержка форм, но мне не удалось найти поддерживающих библиотек. Похоже, это пока только спецификация.

Является ли такая распространенная проблема, как наличие форм, управляемых API, все еще нерешенной с помощью спецификации и инструментов?

Я полностью согласен с тем, что особенно в отношении поддержки обучения клиентов тому, как должны выглядеть запросы, еще предстоит проделать большую работу. Мне известно о гало+json, но, к сожалению, это еще не зарегистрирован в IANA. Как объяснено в HTML-формах другой ответ, к сожалению, поддерживаются только ПОЛУЧИТЬ и ОТПРАВИТЬ

Roman Vottner 23.03.2019 17:53

Говоря о JSON-LD, вы можете взглянуть на Hydra (гидра-cg.com), чтобы описать поддерживаемые операции API и их полезные данные.

aveltens 25.03.2019 10:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
2
480
1

Ответы 1

В вашем примере кажется, что Connections — это ресурсы. Не совсем ясно, действительно ли Orders являются ресурсами. Я предполагаю, что да, но чтобы иметь Order, вам нужны Client и Connection. Итак, чтобы создать Order, вам нужно будет открыть коллекцию, скорее всего, из Client или Connection, возможно, из обоих.

Я думаю, что разрыв связан с мышлением типа «теперь, когда у нас есть список доступных подключений, клиент может выбрать одно из них и создать Order». Это совершенно верно, но это мышление удаленного вызова процедур (RPC), а не REST. Ни один из них объективно не лучше другого, за исключением контекста определенного набора требований проекта, и, как правило, их не следует смешивать вместе.

С мышлением RPC определяется метод создания заказа (например, с использованием OpenAPI), и ожидается, что любые клиенты будут использовать некоторую внеполосную информацию для определения требуемой правильной формы (например, путем чтения спецификации OpenAPI).

С мышлением REST/HATEOAS правильным подходом будет предоставление коллекции Orders из Connection. У каждого Connection в коллекции есть ссылка self и коллекция Orders (ссылка или объект в соответствии с требованиями приложения). Каждый элемент Order имеет ссылку self, и именно здесь указаны возможности. Order — это известный тип (даже с REST/HATEOAS клиент и сервис должны, по крайней мере, согласовать общий словарь), который клиент предположительно знает, как определить. Этот словарь можно определить с помощью любого работающего механизма — json-ld, XSD и т. д.

HATEOAS требует, чтобы результат содержал все, что нужно клиенту для обновления состояния. Не может быть внеполосной информации (кроме общего словаря). Итак, чтобы решить вашу проблему, вам нужно либо предоставить коллекцию Order из Connection, либо разрешить создание Order путем публикации в Connection. Если последнее кажется чем-то вроде взлома, вероятно, так оно и есть.

Например, в HAL-Forms я бы сделал что-то вроде:

{
  "connections": [{
    "id": "aaa",
    "carrier": "Fast Bus",
    "price": 3.20,
    "departure": "2019-04-05T12:30"
    "_links": { 
      "self": { ... }, // link to this connection
      "orders": {} // link to collection of orders for this connection
    }
  },
  , ...],
  "_links": {
    "self": { ... } // link to the collection
  },
  "_templates": { ... } // post/put/patch/delete connection
}

Клиенты переходили по ссылкам на orders и оттуда получали _templates коллекцию, содержащую инструкции по управлению Order ресурсами. Для Order POST, скорее всего, потребуется идентификатор соединения и информация о клиенте. Спецификация HAL-форм определяет свойство регулярного выражения, которое можно использовать для указания типа данных, предоставляемых для любого конкретного элемента формы. Поскольку вы достигли заказа, перейдя по определенному соединению, вы сможете указать в своем _templates для этого заказа, какие именно поля необходимы. например /orders?connectionType=aaa вернет другой набор обязательных свойств, чем /orders?connectionType=bbb, но оба используют одну и ту же self ссылку /orders?connectionType = {type}, и вы должны проверить ее при POST/PUT/PATCH.

Я должен отметить, что Spring-HATEOAS выходит за рамки спецификации HAL-Forms и позволяет использовать несколько _links и _templates. См. эта проблема GitHub.

Может показаться, что HATEOAS/REST требует немного больше работы, чем простой API OpenAPI/RPC, и это действительно так. Но то, от чего вы отказываетесь в простоте, вы приобретаете в гибкости и отказоустойчивости, предполагая хорошо продуманных клиентов. Какой подход является правильным, зависит от множества факторов, большинство из которых не являются техническими (навыки команды, ожидаемые потребители, насколько вы контролируете клиентов, техническое обслуживание и т. д.).

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

gary 09.11.2021 12:37

Другие вопросы по теме