用于创建和更新资源的超媒体友好型REST模式

时间:2014-08-28 19:31:09

标签: rest design-patterns web restful-architecture hypermedia

我正在尝试设计一个充分利用超媒体的RESTful服务 优选地,用户代理应该只知道根URI,以便能够探索服务的所有功能 - 也就是说,我希望它位于maturity model的第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" }
    }
}

foo 资源中的 fooWriter 中加入链接

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" }
    }
}

总而言之,任何可以创建和编辑的资源都可能具有关联的 writer 资源。
通过GET-ting writer ,用户代理可以非常方便的方式创建/编辑资源。
嵌入在编写器中的有效负载会被POST到其目的地和voilà:)

此外,应该有一个 root 容器,其中包含指向新资源的资源及其编写者的链接(请参阅上面示例中的 fooRoot )。



问题是......

......上述模式是否有一个众所周知的名称? ...有没有更好的方法来解决创建 / 编辑问题,其中创建 / 编辑<需要相邻的资源/ em>时间和第三级成熟度仍然“持有”?

一些参考文献:

2 个答案:

答案 0 :(得分:1)

你描述的内容让我想起了create and edit form link relations。但是,如果你正在构建一个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收集资源并通过其链接与parnsip相关:

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

要随后修改胡萝卜,请对返回的URL发出PUT:

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)进行通信&#34; Level 3&#34;使用JSON的REST语义。 HAL简单而强大。我真正喜欢的一个约定是使用关系名称​​作为一个URI,当取消引用时,它直接指向关于该关系的文档及其可以返回的资源。

如果您想使用这样的实时API,我强烈建议您查看HALtalk,这是HAL的实时演示API。

答案 1 :(得分:0)

我们问一个问题。编辑资源需要什么?

  • 使用HTML,您需要一个FORM标记,并在其中包含INPUT标记。

他们描述了什么?

  • 1。)这是一个表格。
  • 2。)它有一个动作IRI。
  • 3。)它有一个方法。
  • 4。)它有内容类型。
  • 5。)它有多个字段,应由用户填写。也许有验证数据。

在你得到的表格中:

  • 6。)整个表格的标题
  • 7.。)输入字段的标签

这与REST客户端需要的几乎相同。

  • 8。)它应该包含已经知道的REST客户端的链接关系,因此客户端将使用它将表单放入正确的上下文中。 (您可以通过HTML使用RDFa执行此操作。)

这就是全部,而不是更少,而不是更多。

现在让我们看看你最喜欢的2种JSON媒体类型的当前选项(还有很多其他JSON超媒体类型,比如集合+ 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词汇,您可以在RDF中为hydra:Link添加操作 - 通常是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”,“created_at”,“title”,“comments”只是您可以使用@context描述的替代名称。

所以在这里,@context描述了表示的上下文和属性名称,如/ctx#create-comment,操作名称,如hydra:CreateResourceOperation描述链接关系8.)。如果您愿意,可以通过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:expectshxdra:returns描述。)因此它是“@context”(我在示例中将其留下了)。因此,通过RDF + hydra vocab,您不需要任何其他内容来描述您的表单。

您可能需要的是定义同一资源的多个表示。

例如,您需要具有创建链接,编辑链接,删除链接等的表示...并且您需要仅包含数据的表示。你可以采取多种方式:

  • 您可以为每个表示定义供应商特定的内容类型。
  • 您可以为资源添加新的IRI,例如/issues/?data-only=1。 (查询是针对IRI的非分层部分,因此它也是资源标识符的一部分。单个资源可以有多个标识符,因此/issues//issues/?data-only=1可以具有单个标识符不能属于多个资源,但我认为这很明显。)因此,在您的情况下,您不必创建编写器和编辑器资源,只需拥有一个具有多个标识符(IRI)的资源就足够了