Я пытаюсь применить 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?
Я проверил разные спецификации и вот что я мог сказать, прочитав спецификации:
"_templates" но в самом шаблоне нет URI. Предполагается, что он работает с собственной ссылкой, которая в моем случае будет / соединения..., а не /заказы."template", поэтому предполагается, что все элементы коллекции имеют одинаковые поля, что не так в моем приложении."actions" подойдет для моего варианта использования, но проект кажется мертвым, и нет поддерживающих библиотек для многих основных языков.Является ли такая распространенная проблема, как наличие форм, управляемых API, все еще нерешенной с помощью спецификации и инструментов?
Говоря о JSON-LD, вы можете взглянуть на Hydra (гидра-cg.com), чтобы описать поддерживаемые операции API и их полезные данные.





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