如何在使用超媒体链接时处理REST资源的更新

时间:2013-03-14 17:13:15

标签: rest hateoas

我正在开发一个REST-ful API,其中的资源相当相关。资源相互引用,可以创建或删除这些引用。我有点不确定如何在使用超链接引用彼此时将资源关联在一起。

一个简单的例子是两个资源,A和B.

Resource A:
  name: integer
  list_b: [list of resource B]

Resource B:
  id: integer
  description: String

现在,A在其文档中不包含B,而是链接到它。使用超媒体时,它可能看起来像这样:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" }
    ]
}

如果用户想要在A列表中添加或删除其中一个B引用,那么考虑到超链接的存在,他们是如何做到的?我希望用户能够在一次PUT操作中更新整个A资源,但输出中没有任何内容表明需要B的值。对我来说,使用这样的内容执行PUT是有意义的:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" },
        { id: 3 },
    ]
}

并接收更新的资源(在响应中),如下所示:

Resource A:
{
    id: 1,
    list_b: [
        { id: 1, href: "https://server/api/b/1" },
        { id: 2, href: "https://server/api/b/2" },
        { id: 3, href: "https://server/api/b/3" }
    ]
}

我担心的是,在更新资源A的list_b时,用户不一定知道要包含在资源中的内容。

在处理从一个资源到另一个资源的超链接时,应该如何创建和更新?是否应允许客户更新部分链接(id),还是应该要求他们更新链接的两个部分?

注意:我知道另一种方法可能是暴露资源A的子网址。它可以将list_b公开为可通过HTTP操作的资源(允许客户端使用POST, PUT,并在列表资源本身上删除)。但是当A包含对其他资源类型的多个引用时,这似乎不太合理。引用另一个字段的每个字段都可能需要一个子网址,如果有10个以上的字段,则该网址不实用,并且需要多个HTTP请求来更新资源。

2 个答案:

答案 0 :(得分:2)

HATEOAS在RESTful接口中将资源连接在一起,这里不清楚您描述的子对象是否真的作为独立资源有意义。 HATEOAS的“AS”部分提醒我们Web页面在Web应用程序中扮演“资源”的角色。每个Web页面实际上都是应用程序状态的交互式表示(在这种情况下,“应用程序”是一个经典的多页Web应用程序),并且到其他资源的超链接为用户提供了到其他应用程序状态的转换。

RESTful Web API,以JavaScript代码而不是人类作为其客户端,自然是面向数据访问的,因此很少有任何资源采用“应用程序状态”的形式,本身。在传统的Web应用程序中,您可以绘制状态转换图并清楚地查看状态之间的连接,从而查看资源之间的连接。在RESTful API中,被动数据资源之间的界限更多地受到客户端/服务器交互效率和其他微妙力量的激励。

你的辅助对象(“B”)在这里真的需要代表作为一流的资源吗?是否存在前端将枚举或以其他方式访问它们的实例,而不依赖于它们参与的聚合(“A”)?

如果答案是“否”,那么它们显然不应该在“A”结构中以文字形式表示。我认为答案是肯定的,但是你也有充分的理由提供你所称的独立资源的所有其他子对象。在这种情况下,有一些路由和控制器形式的接口工作,无论如何都是支持所有这些资源所必需的,因为你的应用程序可能提供了一种独立操作它们的方法,或者至少是查询它们(通过例如你的例子中的超链接)。

在这种情况下,表示“B”对象集合的路径的POST(例如“server / api / b”)可以在响应的“location”标头值中返回一个URL作为创建新资源的POST应该做的。当您的用户以交互方式将新“B”添加到网页上属于“A”的列表时,您的前端可以首先发布新的“B”,并在成功时通过位置标头返回其URL。然后,它可以在将“A”更新为“A”之前将该链接合并到其“A”对象中的列表表示中。

ID值有点皱纹,因为您将通过从URL文本中提取ID值来破坏后端的封装。真正的HATEOAS狂热者使他们的RESTful API产生混淆,散列或其他无法理解的URL,专门用于阻止客户端的这种封装破坏。更好的是,新“B”对象的POST在其响应主体中返回新“B”对象的完整表示,包括其ID,以便客户端可以重构完整对象并从中提取ID,从而缩小与资源本身的耦合,而不是通过它获取的RESTful接口的细节。

答案 1 :(得分:1)

您还应该查看LINK method

LINK /ResourceA/1 HTTP/1.1
Link: <http://example.com/ResourceB/3>; rel="list_b"
...

204 Yeah Fine, Whatever

这告诉/ ResourceA / 1使用“list_b”关系链接到/ ResourceB / 3。