Гипермедиа-дружественный шаблон REST для создания и обновления ресурса
Я пытаюсь разработать RESTful-сервис, который хорошо использует Hypermedia.
Предпочтительно, чтобы пользовательский агент знал только корневой URI, чтобы иметь возможность исследовать все функциональные возможности службы, то есть я хотел бы, чтобы он находился на 3-м уровне в модели зрелости.
Теперь пользовательский агент должен иметь возможность создавать некоторые ресурсы, а также редактировать их позднее. Во время создания / редактирования пользовательскому агенту необходим доступ к некоторым другим ресурсам / перечислениям.
ресурсfoo:
{
"category" : "category chosen from an enumeration of possible categories",
"color" : "color chosen from an enumeration of possible colors",
"aRelatedResource" : "resource identifier from chosen from a collection"
}
Учитывая ранее упомянутые требования, я придумал следующую схему:
Есть ресурс fooRoot:
{
// no properties, only links
"_links" : {
"foos" : { "href" : "URI-to-foos" },
"fooCreator" : { "href" : "URI-to-plain-fooWriter" }
}
}
Включите ссылку на fooWriter в ресурс foo:
ресурсfoo:
{
"category" : "category chosen from an enumeration of possible categories",
"color" : "color chosen from an enumeration of possible colors",
"aRelatedResource" : "resource identifier from chosen from a collection",
"_links" : {
"self" : {...},
"fooEditor" : { "href" : "URI-to-fooWriter-initialized-for-current-foo" }
}
}
FooWriter будет выглядеть следующим образом:
{
"fooPayload" : {
"category" : "NULL or pre-initialized",
"color" : "NULL or pre-initialized",
"aRelatedResource" : "NULL or pre-initialized"
},
"_links" : {
"fooPayloadDestination" : { "href" : "URI-to-foos-or-foo" },
"categoryEnum" : { "href" : "URI-to-categories" },
"colorEnum" : { "href" : "URI-to-colors" },
"availableResourcesToRelateWith" : { "href" : "some-other-URI" },
....
.... and even something useful for pre-validation etc.
"validator" : { href : "URI-to-some-resource-or-service" }
}
}
Подводя итог, любой ресурс, который может быть создан и отредактирован, может иметь связанный ресурс записи.
ПОЛУЧАЯписателя, пользовательский агент может создавать / редактировать ресурс довольно удобным способом.
Полезная нагрузка, встроенная в устройство записи, отправляется по почте к месту назначения и вуаля:)
Кроме того, должен быть корневой контейнер, содержащий ссылки как на ресурс, так и на его средство записи новых ресурсов (см. FooRoot в приведенном выше примере).
Вопросы...
... У шаблона, описанного выше, есть известное имя?
... есть ли лучший способ решить проблему создания / редактирования, когда смежные ресурсы требуются во время создания / редактирования, а 3-й уровень зрелости все еще "держится"?
Некоторые ссылки:
2 ответа
То, что вы описываете, напоминает мне немного о создании и редактировании формы связи ссылок. Однако, если вы создаете API, его использование довольно ограничено, так как вам нужно, чтобы кто-то программировал его независимо от того, как он определен.
На мой взгляд, самый простой способ организовать приведенный выше пример - определить корневое меню следующим образом:
GET / HTTP/1.1
Accept: application/hal+json
----
HTTP/1.1 200 OK
Content-Type:application/hal+json
{
"_links" : {
"plants" : { "href" : "/plants" }
}
}
plants
отношение будет содержать коллекцию растительных ресурсов, определенных данным типом носителя (скажем, это application/vnd.biology-example-org.plant
):
GET /plants HTTP/1.1
Accept: application/hal+json
----
HTTP/1.1 200 OK
Content-Type:application/hal+json
{
"_links" : {
"self" : { "href" : "/plants" },
"plant": [
{
"href" : "/plants/parsnip",
"title" : "The Parsnip",
"type" : "application/vnd.biology-example-org.plant+json"
}
]
}
}
Чтобы добавить новое растение в коллекцию, связанную с пастернаком, POST к plants
Сбор ресурса и ссылки на парнсип по его ссылке:
POST /plants HTTP/1.1
Content-Type: application/vnd.biology-example-org.plant+json
{
"title" : "The Carrot - a cousin of the Parsnip",
"category" : "vegetable",
"color" : "orange",
"related" : [ "/plants/parsnip" ]
}
----
HTTP/1.1 201 Created
Location: http://biology.example.org/plants/carrot
Чтобы впоследствии изменить морковь, введите PUT для URL-адреса, который был возвращен:
PUT /plants/carrot HTTP/1.1
Content-Type: application/vnd.biology-example-org.plant+json
{
"title" : "The Carrot - the orange cousin of the Parsnip",
"category" : "vegetable",
"color" : "orange",
"related" : [ "/plants/parsnip" ]
}
----
HTTP/1.1 200 OK
В приведенном выше примере используется язык приложений гипертекста (HAL) для передачи семантики REST уровня 3 с использованием JSON. HAL прост, но очень силен. Одно из соглашений, которое мне действительно нравится, заключается в использовании имени отношения в качестве URI, который при разыменовании указывает непосредственно на документацию об этом отношении и на ресурсы, которые он может вернуть.
Если вы хотите поиграть с таким живым API, я настоятельно рекомендую взглянуть на HALtalk, который является живым демо-API HAL.
Давайте зададим вопрос. Что нужно для редактирования ресурса?
- С HTML вам нужен тег FORM и теги INPUT в них.
Что они описывают?
- 1.) Это форма.
- 2.) Имеет действие IRI.
- 3.) У него есть метод.
- 4.) Имеет тип контента.
- 5.) Имеет несколько полей, которые должен заполнять пользователь. Возможно с данными проверки.
В форме вы получили:
- 6.) название для всей формы
- 7.) метка для полей ввода
Это почти то же самое, что нужно клиенту REST.
- 8.) Он должен содержать отношение ссылки для клиента REST, которое он уже знает, поэтому клиент будет использовать его, чтобы поместить форму в правильный контекст. (Вы можете сделать это с помощью RDFa с помощью HTML.)
Вот и все, не меньше, не больше.
Теперь давайте посмотрим ваши текущие параметры по моим 2 любимым типам мультимедиа JSON (есть много других типов гипермедиа JSON, таких как collection+json, shiren и т. Д.).
HAL+JSON
С помощью HAL вы можете определять встроенные ресурсы и добавлять ссылки на них.
{
"_links": {
.. *snip* ..
},
"_embedded": {
"manufacturer": {
"_links": {
"self": { "href": "/manufacturers/328764" },
"homepage": { "href": "http://hoverdonkey.com" }
},
"name": "Manufacturer Inc."
},
"review": [
{
"_links": {
"self": { "href": "/review/126" },
"customer": { "href": "/customer/fred", "title": "Fred Wilson" }
},
"title": "Love it!",
"content": "I love this product. I also bought a Hover Donkey with it.",
"rating": 10
},
...
]
},
"name": "A product",
"weight": 400,
.. *snip* ..
}
_Link описывает здесь 1.), href описывает здесь 2.), заголовок ссылки описывает 6.), отношение ссылки описывает 3.) и 8.). Нам нужен тип контента 4.) и метаданные о необходимых полях: валидация, метки 5.) и 7.).
Теперь, какой у нас есть выбор?
- Мы можем несколько расширить HAL+JSON и добавить свойство fields к каждой ссылке, или
- мы можем добавить специфические типы контента для описания полей.
JSON-LD + Hydra Vocab
С помощью Hydra Vocab вы можете добавить операции к вашему hydra:Link
s в вашем RDF - обычно JSON-LD - документе.
{
"@context": {
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"hydra": "http://www.w3.org/ns/hydra/core#",
"vocab": "/vocab#",
...
"title": {
"@id": "vocab:Issue/title",
"@type": "hydra:property",
"rdfs:range": "xsd:string",
"rdfs:label": "Issue title"
},
"comments": {
"@id": "vocab:comments",
"@type": "hydra:Link",
"hydra:supportedOperation": [
{
"@id": "vocab:create-comment",
"@type": "hydra:CreateResourceOperation",
"rdfs:label": "Creates a new comment",
"hydra:method": "POST",
"hydra:expects": "vocab:Comment",
"hydra:returns": "vocab:Comment"
}
]
}
},
"@id": "/issues/cso29ax",
"id": "cso29ax",
"created_at": "2012-12-10 12:45",
...
"title": "Some random issue",
"comments": {
"@id": "/issues/cso29ax/comments/"
}
}
Ofc. на практике вы бы переместили всю часть @context в отдельный файл под IRI /vocab
, но так проще проверить, что он делает. К документам RDF у вас есть тематические, предикатные, целевые триплеты. Например, здесь: /issues/cso29ax
, /ctx#Issue/title
, "Some random issue"
может быть триплет Таким образом, "id", "create_at", "title", "comments" - это просто альтернативные имена, которые вы можете описать с помощью @context
,
Так что здесь @context
описывает контекст представления и имя свойства как /ctx#create-comment
и имя операции, как hydra:CreateResourceOperation
опишите ссылку-отношение 8.). Ofc вы можете использовать iana
Воаб по описанию /ctx#create-comment
если ты хочешь. Это зависит от вас и возможностей вашего REST-клиента. hydra:Link
описывает, что это форма 1.). @id: "/issues/cso29ax/comments/"
описывает действие IRI 2.). rdfs:label
описывает заголовок формы и метки полей ввода 6.) и 7.) (может быть многоязычным). Тип содержимого всегда JSON+LD 4.). Метод HTTP описывается hydra:method
3.), данные проверки и т.д... описаны hydra:expects
а также hxdra:returns
каждого свойства 5.), поэтому он является частью "@context" (я оставил это в примере). Таким образом, благодаря RDF + hydra vocab вам больше ничего не нужно для описания ваших форм.
Что вам, вероятно, нужно, это определить несколько представлений одного и того же ресурса.
Например, вам нужно представление со ссылкой для создания, редактировать ссылку, удалить ссылку и т. Д. И вам нужно только представление с данными. Вы можете сделать это несколькими способами:
- Вы можете определить тип контента для конкретного поставщика для каждого из представлений.
- Вы можете добавить новый IRI для вашего ресурса, например
/issues/?data-only=1
, (Запрос относится к неиерархической части IRI, поэтому он также является частью идентификаторов ресурса. Один ресурс может иметь несколько идентификаторов, поэтому/issues/
и/issues/?data-only=1
может иметь тот же ресурс. Один идентификатор не может принадлежать нескольким ресурсам, но я думаю, что это очевидно.) Так что в вашем случае вам не нужно создавать ресурсы для записи и редактирования, достаточно иметь один ресурс с несколькими идентификаторами (IRI).