应该如何为 RESTful JSON 集合实现 HATEOAS 风格的链接?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17877220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 19:58:50  来源:igfitidea点击:

How should HATEOAS-style links be implemented for RESTful JSON collections?

jsonresthateoas

提问by thomas-peter

To keep things simple and to avoid nameing collisions, I've been bundling links in my record resources like this...

为了简单起见并避免命名冲突,我一直在像这样在我的记录资源中捆绑链接......

{
    id: 211,
    first_name: 'John',
    last_name: 'Lock',
    _links: [
        { rel: 'self', href: 'htttp://example.com/people/211' }
    ]
}

However, I can't work out how to implement links in collections. I have spent a long time trawling around the web for examples and other than using the not so lean HALI'm unable to reconcile my problem.

但是,我无法弄清楚如何在集合中实现链接。我花了很长时间在网上搜索示例,除了使用不太精简的HAL 之外,我无法解决我的问题。

[
    {id:1,first_name:.....},
    {id:2,first_name:.....},
    {id:3,first_name:.....}, 
    "_links": "Cant put a key value pair here because its an-array" 
]

Which means I have to wrap up the array in a container object.

这意味着我必须将数组包装在一个容器对象中。

[
    people: [ {id:1,first_name:.....} ],
    links: [ { rel:parent, href:.... ]
]

But is is different to the singular resource so I'm going to make the record behave like the collection and wrap it up in a container....

但是 is 与单一资源不同,所以我要让记录表现得像集合并将它包装在一个容器中......

{
    person: {
        id: 211,
        first_name: 'John',
        last_name: 'Lock',
        _links: 
    },
    links:[
        { rel: 'self', href: 'htttp://example.com/people/211' }
    ] 
}

On the surface this seems like quite a neat solution. The resulting JSON is one level deeper but HATEOAS has been implemented, so that's all good right? Not at all. The real sting comes when I go back to the collection. Now that the single resource has been wrapped up in a container in order to be consistent with the collection, the collection must now be changed in order to reflect the changes. And this is where it gets ugly. Very ugly. Now the collection looks like this...

从表面上看,这似乎是一个非常巧妙的解决方案。生成的 JSON 更深一层,但 HATEOAS 已经实现,所以一切都很好,对吗?一点也不。当我回到收藏时,真正的刺痛就来了。既然单个资源已被包装在容器中以与集合保持一致,现在必须更改集合以反映更改。这就是它变得丑陋的地方。十分难看。现在这个集合看起来像这样......

{
    "people": [
        {
            "person": {
                ....
            },
            "links" : [
                {
                    "rel": "self",
                    "href": "http://example.com/people/1"
                }
            ]
        },
        {
            "person": {
                ....
            },
            "links" : [
                {
                    "rel": "self",
                    "href": "http://example.com/people/2"
                }
            ]
        }
    ],
    "links" : [
        {
            "rel": "self",
            "href": "http://example.com/people"
        }
    ]
}

Is there a simpler solution to implementing HATEOAS for collections? Or should I kiss HATEOAS goodbye for forcing me to over complicate the data structure?

是否有更简单的解决方案来为集合实施 HATEOAS?或者我应该告别 HATEOAS 强迫我过度复杂化数据结构?

回答by Nicholas Shanks

Please don't dismiss HAL so quickly just because it looks a little bloated (in its JSON form, it's quite minimal).

请不要仅仅因为 HAL 看起来有点臃肿(在它的 JSON 形式中,它非常小)就这么快地驳回它。

HAL is to JSON what HTML is to plain text.

HAL 之于 JSON 就像 HTML 之于纯文本。

It adds hyperlinks. You need hyperlinks and a commonly understood representation format (such as HAL or Collection+JSON) for REST. You also need HATEOAS for REST, without HATEOAS it isn't REST! HATEOAS requires hyperlinks of course.

它添加了超链接。您需要超链接和 REST 的普遍理解的表示格式(例如 HAL 或 Collection+JSON)。您还需要 HATEOAS 进行 REST,没有 HATEOAS 就不是 REST!HATEOAS 当然需要超链接。

In your case, you are trying to build a collection resource. The IANA-registered relationfor that is "item" (with reverse relation "collection"). Here is the representation in HAL for a People collection:

在您的情况下,您正在尝试构建一个集合资源。在IANA注册的关系,因为这是“项目”(具有反向关系,“收藏”)。以下是 People 集合在 HAL 中的表示:

{
    "_links": {
        "self": { "href": "http://example.com/people" },
        "item": [
            { "href": "http://example.com/people/1", "title": "John Smith" },
            { "href": "http://example.com/people/2", "title": "Jane Smith" }
        ]
    },
    "_embedded": {
        "http://example.com/rels#person": [
            {
                "first_name": "John",
                "last_name": "Smith",
                "_links": {
                    "self": { "href": "http://example.com/people/1" },
                    "http://example.com/rels#spouse": { "href": "http://example.com/people/2" }
                }
            },
            {
                "first_name": "Jane",
                "last_name": "Smith",
                "_links": {
                    "self": { "href": "http://example.com/people/2" },
                    "http://example.com/rels#spouse": { "href": "http://example.com/people/1" }
                }
            }
        ]
    }
}

Note:

笔记:

  • The primary data for this collection comes from _links.item[]. These are the items in the collection. The full (or at least some additional) data for each item is available in the _embeddedarray. If the client needs these additional data, it must find them by searching through _embedded[n]._links.self.hreffor each n. This is a design constraint of HAL. Other hypermedia representation formats have similar constraints (though perhaps going in the other direction).

  • I have added a titlevalue for each member of the itemarray. This can appear between the opening and closing anchor tags if rendering to HTML, or as the text of a menu item in the client, without need for further processing of the representation by the client.

  • There are no ID parameters. All references to other resources are exposed as hyperlinks. A client should not have to "build" a URL by gluing an ID into a URL at some pre-defined place. This constitutes out-of-band information which inhibits independent changes to the client and server.

  • All of your hyperlinks should be absolute, since relative URLs may cause problems. All of your relations should be either listed on that IANA page or use a URI to define them. Ideally, that URI should be a dereferencable HTTP URL with documentation about the relation at the other end.

  • 此集合的主要数据来自_links.item[]. 这些是集合中的项目。每个项目的完整(或至少一些附加)数据在_embedded数组中可用。如果客户端需要这些附加数据,它必须通过搜索_embedded[n]._links.self.href每个n. 这是 HAL 的设计约束。其他超媒体表示格式也有类似的限制(尽管可能相反)。

  • titleitem数组的每个成员添加了一个值。如果呈现为 HTML,或者作为客户端中菜单项的文本,则这可以出现在开始和结束锚标记之间,而无需客户端对表示进行进一步处理。

  • 没有 ID 参数。对其他资源的所有引用都公开为超链接。客户端不应该通过将 ID 粘贴到某个预定义位置的 URL 来“构建”一个 URL。这构成了禁止对客户端和服务器进行独立更改的带外信息。

  • 您的所有超链接都应该是绝对链接,因为相对 URL 可能会导致问题。您的所有关系都应该列在该 IANA 页面上,或者使用 URI 来定义它们。理想情况下,该 URI 应该是一个可取消引用的 HTTP URL,并在另一端提供有关该关系的文档。

回答by Nicholas Shanks

回答by Morgan

First, I don't believe API's which have endpoints that return collections (JSON arrays) are truly RESTful. However, most "REST" API's bend the rules here.

首先,我不相信具有返回集合(JSON 数组)的端点的 API 是真正的 RESTful。然而,大多数“REST”API 在这里违反了规则。

I recently developed a REST API for the NextBus XML feedcalled restbusthat returns collections from some endpoints while using HATEOAS style hypertext links. Here is a sample of the structure I used:

我最近开发了一个REST API NextBus XML饲料的Restbus,尽管使用HATEOAS风格的超文本链接从一些端点回报集合。这是我使用的结构示例:

{
  // ... SF-Muni resource from restbus API ...

  _links: {
    self: {
      href: "http://localhost:3535/agencies/sf-muni",
      type: "application/json",
      rel: "self",
      rt: "agency",
      title: "Transit agency 'sf-muni'."
    },
    to: [
      {
        href: "http://localhost:3535/agencies/sf-muni/routes",
        type: "application/json",
        rel: "describedby",
        rt: "route",
        title: "A collection of routes for transit agency 'sf-muni'."
      },
      {
        href: "http://localhost:3535/agencies/sf-muni/vehicles",
        type: "application/json",
        rel: "describedby",
        rt: "vehicle",
        title: "A collection of vehicles for transit agency 'sf-muni'."
      }
    ],
    from: [
      {
        href: "http://localhost:3535/agencies",
        type: "application/json",
        rel: "bookmark",
        rt: "agency",
        title: "A collection of transit agencies. This is the API root!"
      }
    ]
  }

}

It doesn't try to follow any of the popular JSON linking strategies out there (or their associated media types) like HALetal. because they don't appear to be on the IETF Standards Track(yet). Instead the link object target attributesand link relation valuesmeet RFC 5988 Web Linking specificationsas much as possible.

它不会尝试遵循任何流行的JSON链接策略,在那里(或其相关媒体类型)一样的HAL。因为它们似乎还没有在IETF 标准轨道上(还)。相反,链接对象目标属性链接关系值尽可能满足RFC 5988 Web 链接规范

You can see more details about the restbus hypertext link structure.

您可以查看有关restbus 超文本链接结构的更多详细信息。

回答by swist

You may try to look at Restful object specification. That guys create concrete API. As I don't like the whole idea, there is a many practical solutions you can grab from them.

您可以尝试查看Restful object specification。那些家伙创建了具体的 API。由于我不喜欢整个想法,因此您可以从中获取许多实用的解决方案。