我正在尝试设计一个充分利用超媒体的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>时间和第三级成熟度仍然“持有”?
一些参考文献:
答案 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)
我们问一个问题。编辑资源需要什么?
他们描述了什么?
在你得到的表格中:
这与REST客户端需要的几乎相同。
这就是全部,而不是更少,而不是更多。
现在让我们看看你最喜欢的2种JSON媒体类型的当前选项(还有很多其他JSON超媒体类型,比如集合+ json,shiren等等。)。
通过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。)。
现在我们有什么选择?
通过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:expects
和hxdra:returns
描述。)因此它是“@context”(我在示例中将其留下了)。因此,通过RDF + hydra vocab,您不需要任何其他内容来描述您的表单。
例如,您需要具有创建链接,编辑链接,删除链接等的表示...并且您需要仅包含数据的表示。你可以采取多种方式:
/issues/?data-only=1
。 (查询是针对IRI的非分层部分,因此它也是资源标识符的一部分。单个资源可以有多个标识符,因此/issues/
和/issues/?data-only=1
可以具有单个标识符不能属于多个资源,但我认为这很明显。)因此,在您的情况下,您不必创建编写器和编辑器资源,只需拥有一个具有多个标识符(IRI)的资源就足够了