Гипермедиа-дружественный шаблон 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:Links в вашем 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).
Другие вопросы по тегам