【问题标题】:How should HATEOAS-style links be implemented for RESTful JSON collections?应该如何为 RESTful JSON 集合实现 HATEOAS 样式的链接?
【发布时间】:2013-07-26 09:04:45
【问题描述】:

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

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

但是,我不知道如何在集合中实现链接。我花了很长时间在网上搜索示例,除了使用不太精简的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" 
]

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

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

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

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

从表面上看,这似乎是一个非常巧妙的解决方案。生成的 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"
        }
    ]
}

有没有更简单的解决方案来为集合实施 HATEOAS?或者我应该和 HATEOAS 告别,因为它迫使我过度复杂化数据结构?

【问题讨论】:

    标签: json rest hateoas


    【解决方案1】:

    请不要仅仅因为 HAL 看起来有点臃肿(以 JSON 形式,它非常小)就这么快就将其关闭。

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

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

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

    {
        "_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" }
                    }
                }
            ]
        }
    }
    

    注意:

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

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

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

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

    【讨论】:

    • 附带问题:为什么要在 _links_embedded 属性前加上下划线?它是某些命名约定的一部分吗?
    • @sp00m 它是 HAL 规范的一部分,它将数据与元数据分开:stateless.co/hal_specification.html
    • 我正在设置 GET https://,但是 spring HATEOAS 只给了我 http://,我使用的是 nginx/1.6.0。
    • @user4567570 我不使用 Spring,但我认为您的路由已配置为不安全,Spring 只是为它知道的路由生成 URL。..
    【解决方案2】:

    似乎 JSON 链接还不是一个已解决的问题。有几个竞争者:

    参考文献

    【讨论】:

      【解决方案3】:

      首先,我不认为具有返回集合(JSON 数组)的端点的 API 是真正的 RESTful。但是,大多数“REST”API 都违反了规则。

      我最近为 NextBus XML feed 开发了一个名为 restbus 的 REST API,它在使用 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!"
            }
          ]
        }
      
      }
      

      它不会尝试遵循任何流行的 JSON 链接策略(或其关联的媒体类型),例如 HAL et al。因为它们似乎还没有出现在IETF Standards Track 上。相反,链接对象目标属性链接关系值尽可能满足RFC 5988 Web Linking specifications

      您可以查看有关restbus hypertext link structure的更多详细信息。

      【讨论】:

      • 为什么您认为具有返回集合(JSON 数组)的端点的 API 不是真正的 RESTful?我对你的意见很感兴趣。
      • HAL 确实有一个 IETF standard draft,尽管它即将在几天后到期。
      • @Tivie 我可能在这里吹毛求疵,但对我来说,REST API 端点应该返回一个且只有一个资源的表示。一个数组可能返回一个以上的资源,并且在定义方面有点混乱。与其返回资源数组,不如创建一个新资源,其中包含在数组中找到的聚合信息。
      • 您的输入很有帮助,但我认为资源集合本身仍然是资源。就像一个资源(例如/cars/1)可能有“子资源”(/cars/1/make⟼“劳斯莱斯”),它们可能是父资源中的字段,也可能根本没有在父资源中公开。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-27
        • 1970-01-01
        • 2019-04-13
        • 1970-01-01
        相关资源
        最近更新 更多