HAL - 如果链接在主体中,是否违反了 HAL format/standard?
HAL - is it a violation to the HAL format/standard if links are in the main body?
根据 HAL 标准(参见 here and here),指向其他资源的链接应放在特定的嵌入部分。
例如,这不是有效的 HAL,我的理解正确吗?
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
"_links": {
"subtitles": {
"href": "/movies/123/subtitles"
}
}
},{
"id": "456",
"title": "Movie title 2",
"_links": {
"subtitles": {
"href": "/movies/456/subtitles"
}
}
}
],
"_links": {
"self": {
"href": "/movies"
}
}
}
上述 JSON 无效 HAL 的原因是链接应放置在链接到ID在主体中。所以正确的方法是:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
},{
"id": "456",
"title": "Movie title 2",
}
],
"_embedded": {
"movies": [
{
"id": "123",
"_links": {
"href": "movies/123/subtitles"
}
},
{
"id": "456",
"_links": {
"href": "movies/456/subtitles"
}
}
]
}
"_links": {
"self": {
"href": "/movies"
}
}
}
以上都正确吗?
谢谢
正如在您发布的 specs 中所观察到的,您可以链接 and/or 嵌入式资源:
资源的链接应作为该资源的 属性:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
"_links": {
"subtitles": {
"href": "/movies/123/subtitles"
}
}
}, {
"id": "456",
"title": "Movie title 2",
"_links": {
"subtitles": {
"href": "/movies/456/subtitles"
}
}
}
]
}
备选方案 是直接嵌入电影的字幕资源:
{
"movies" : [
{
"id" : "123",
"title" : "Movie title 1",
"_embedded" : {
"subtitles" : [{
"name" : "movie 1 subtitles"
}
]
}
}, {
"id" : "456",
"title" : "Movie title 2",
"_embedded" : {
"subtitles" : [{
"name" : "movie 2 subtitles"
}
]
}
}
]
}
“_Links”属性 必须位于 资源对象 的根目录中。 资源对象可能在根目录中,也可能在_embedded对象.
中
我怀疑一些令人困惑的地方来自于将“_embedded”键指向数组。仅当您想要表示相关资源的多个实例时才这样做。
在示例中,键是 movies
,这表明您正在嵌入代表多部电影的资源对象。但是,该数组表示有多个嵌入的资源对象。每个资源对象是一部电影。
通过将密钥名称更改为 "movie",您将得到:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
},{
"id": "456",
"title": "Movie title 2",
}
],
"_embedded": {
"movie": [
{
"id": "123",
"_links": {
"href": "movies/123/subtitles"
}
},
{
"id": "456",
"_links": {
"href": "movies/456/subtitles"
}
}
]
}
"_links": {
"self": "https://example.org/movielist"
}
}
所以,现在您有一个 "movie-list" 资源对象的表示,并且您已经为 "movie-list" 中的每个项目嵌入了一堆 "movie" 资源对象。 "movie"指向的每个资源对象都有一个“_links”属性相关信息。我假设 "subtitles" link 应该是一个自我 link.
打算将其用作使用 hal 重新设计的案例研究。 @darrel millers 的回答很好,但不是很好,有些事情我认为应该澄清。这会很长。
最大的问题是上下文是什么...IE,您要返回的资源是什么。你所拥有的只是某种与电影有关的东西。就像假设这是一个搜索结果......与电影有关系是错误的方法......因为电影与搜索结果 "top level resource" 作为一个项目相关。所以它应该更像是
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
}
}
但是这种结构对于客户端构建一个 UI 来说是昂贵的,因为它必须进行 3 次调用..遵循 N+1 规则(1 用于结果集..然后 N 用于每个结果)因此诞生了_embedded,它只是超文本预取模式的 hal 实现(在 http2 中,服务器实际上可以将每个结果作为它自己的文档发送,并且客户端的缓存将 pre-filled 包含这些结果,你会' 一定需要 _embedded)。该结构看起来更像这样:
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
_embedded : {
item : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
获得 3 个资源的 1 个 http 请求非常棒。 1 个请求不是 N+1。谢谢哈尔!
那为什么是项目?好吧,搜索结果是否只包含电影……这不太可能……即使今天确实如此……您是否希望它明天只包含电影……这是非常狭窄的,这种结构是您的合同必须永远保持基本。但是您的 UI 真的很想将结果显示为电影。我添加的配置文件 link 是为了......客户端使用配置文件 link 来了解它当前正在处理的资源是什么......以及它可以使用哪些字段来构建 UI。一个体面的客户端在处理 collection 时会显示它可以显示的配置文件......而只是忽略它不能显示的配置文件(可能会记录警告)。由客户端开发人员升级他们的应用程序以支持新配置文件……不相信我吗?想一想 Web 浏览器如何解析它在 html 中不理解的标签...在您的 html 文档中添加一个 <thing-not-invented-yet></think-not-invented-yet>
并查看一个好的客户端如何工作。
你应该注意的另一件事是我不使用 self links 但规范..多年来我改变了我对此的立场。最近我默认使用 canonical 并且我只在维护目标资源的版本时使用 self 并且将嵌入的 object link 嵌入到特定版本很重要。这在我的经验中是非常罕见的。我告诉客户在它存在时跟随自己,否则在导航到嵌入项目时遵循规范。这使服务器可以完全控制它想要将客户端带到哪里。顶级资源,在这种情况下,结果仍然应该有一个自我......在这种情况下,它是有意义的,因为 soem 随机搜索可能没有规范 link......除非它是一个非常常见的关键字搜索...那么它可能应该因为其他用户可以使用相同的 url.
让我们花点时间谈谈为什么 item
作为 rel...因为这真的很重要。既然是搜索结果..为什么不让 rel 为 result
。答案很简单。result
不是 IANA link 注册表的成员 https://www.iana.org/assignments/link-relations/link-relations.xhtml,因此 result
完全无效...现在您可以 "namespace" 您的扩展名与 my:result
或 our:result
相关("namespace" 由您决定,这些只是示例)但是如果 IANA 注册表中已经存在一个非常好的扩展名,为什么还要费心呢。 .而且确实 item
。
我们来谈谈 items
与 item
(或 x:movies
与 x:movie
)。好吧 items
也不在 IANA 中..所以它必须是 x:items
但是让我们想想为什么不这样做。如果我们的结果文档以 HTML 表示,它看起来像这样(为简洁起见,忽略我丢失的 body 头部等而不是 well-formedness):
<html>
<title>Results for search XXXXX</title>
<a rel="item" href="https://host.com/url/to/movie/result/one" >A Great Movie</a>
<a rel="item" href="https://host.com/url/to/movie/result/two" >A Great Movie</a>
</html>
这是与第一个示例相同的资源(没有嵌入子资源)。仅表示为 text/html
而不是 application/hal+json
。如果我在这里迷失了你(这是大多数人真正感到困惑的地方,我能提供的最好的办法就是在 https://www.youtube.com/watch?v=u_pZBBELeEQ 上观看我对此的演讲)很明显每个目标资源的适当关系是一个单一的项目而不是一组 ITEMS。每个 link 都针对一个项目(或一部单部电影)。
HAL 有一个陷阱,将其视为 JSON,这会导致像 movies
机器可读或更好的评论那样的陈述。让我继续用例中的 HTML 表示来解释这是如何发生的。
当客户端解析此文档以查找 item
link 时,它必须解析每个 a
标记并过滤到仅存在 rel="item"
属性的标记。那是 "full table scan"..我们如何摆脱这些?我们创建一个索引。 JSON 的结构中内置了索引的概念。它是一个带有数组值的键。 index : [ {entry 1}, {entry 2} ]
。 HAL 的作者知道检索 links(在 _links 或 _embedded 中预取的那些)的最常见方法是通过关系..所以他构建了他的规范,使得 rel 被索引。秒当你看到:
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
知道这是真的
_links : {
self : { rel: "self", href: "https://host.com/search/with/params/XXXXX"},
item : [
{ rel:"item", href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ rel:"item", href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
因为 rel 是 LINK OBJECT 的属性而不是资源。但是 http 上的字节很昂贵(gzip 会摆脱这个)并且开发人员不喜欢冗余(另一个主题)所以当我们有 hal 时我们省略 rel 属性,因为 HAL 结构已经使 rel apparent.虽然当你的解析器遇到这个时它并不是真正的 apparent:
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"}
什么关系?您必须从 parent 节点传递它。这一直很丑陋...无论如何,所有这些都是为了表明 HAL 中通常消除了冗余。一旦消除了这种冗余,很容易将该索引键更改为复数形式 items
但要知道这意味着您说的是 link (一旦冗余被放回)将是 {rel: "items", href : "https://host.com/url/to/movie/result/one", title : "A great Movie"}
并且这显然是错误的..link 不是很多项目...只有一个。
所以在这种情况下删除冗余可能不是最好的..但它是有好处的,HAL 遵循 _links 和 _embedded 的模式,这就是我们要在搜索中做的事情结果..鉴于所有 item
links 都没有 pre-fetched 并且作为 _embedded 存在,将它们保留在 _links 中并不重要。因此它应该是这样的:
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"}
},
_embedded : {
item : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
现在我们有一个很好的搜索结果,包括 2 部电影(并且将来可以在不违反合同的情况下包括更多的东西)。注意:如果您曾经使用 JUST _links 而没有使用 _embedded...您不能删除 _links 因为一些客户端依赖于它们的存在..所以最好早点想到这些东西......认为一个表现良好的客户在使用资源的 HAL 表示时应该总是在 _links 之前检查 _embedded......所以你真的要知道你的所有客户是否都是表现良好。
好的,让我们转到 x:movie
是正确关系的情况..如果顶级资源是演员,那可能会很好。所以像:
{
Name : "Paul Bettany"
_links : {
canonical : { href: "https://host.com/paul-bettany"},
"x:movie" : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
],
"x:spouse" : { href: "", title: "Jennifer Connely"}
},
_embedded : {
"x:movie" : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
注意:我在顶层使用了 canoncial 而不是 self,因为 actor 是 long-lived 资源..那个 actor 将永远存在..并且 actor 没有版本。为了完整起见,我在 _links 和 _embedded 中都保留了 x:movie,但实际上我不会在 _item 中保留它们。我还将它们保留在 _links 中以显示 x:movie 的原因,以便您可以将其与 x:spouse 区分开来(在我们搜索结果的情况下,语义差异没有意义开始于)。最后,值得注意的是我嵌入了 x:movie
而不是 x:spouse
这只是为了说明它不是非此即彼的事情。您可以 pre-fetch/embed 您的用例所需的 link。事实上,我经常根据客户的身份嵌入内容。也就是说,我知道 iOS 可以显示 android 不能显示的内容。
抛开那些注释,我来这里的原因是我想明确表示您没有也不应该拥有该电影:您拥有的数据字段...仅依赖于 _embedded 中的电影数据。你说 soemthign 就像将电影中的值与 _links 或 _embedded 中的值相匹配......你不应该那样做......那没有任何意义。电影是一种资源...使用电影的 linked 资源而不是某些数据字段。您需要尽早决定什么是资源,什么是数据。我最好的提示是,如果某事物具有 link 关系..那么它就是一种资源。在我的演讲中,我使用更广泛的术语(超媒体控件)对此进行了更多详细介绍,但我不想在这里讨论。
最后一点..在超媒体应用程序中,如果您公开内部 ID 字段,您知道自己做错了什么..正如您在此处所做的那样。这应该是一个巨大的危险信号,表明出现了问题。您描述的 id 的用例是将数据字段电影与 _embedded x:movie 匹配。如前所述......你不应该这样做......并且 id 字段的存在应该让你陷入这种不良做法。
我被要求在这里回答..所以我希望这对你有所帮助。
根据 HAL 标准(参见 here and here),指向其他资源的链接应放在特定的嵌入部分。
例如,这不是有效的 HAL,我的理解正确吗?
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
"_links": {
"subtitles": {
"href": "/movies/123/subtitles"
}
}
},{
"id": "456",
"title": "Movie title 2",
"_links": {
"subtitles": {
"href": "/movies/456/subtitles"
}
}
}
],
"_links": {
"self": {
"href": "/movies"
}
}
}
上述 JSON 无效 HAL 的原因是链接应放置在链接到ID在主体中。所以正确的方法是:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
},{
"id": "456",
"title": "Movie title 2",
}
],
"_embedded": {
"movies": [
{
"id": "123",
"_links": {
"href": "movies/123/subtitles"
}
},
{
"id": "456",
"_links": {
"href": "movies/456/subtitles"
}
}
]
}
"_links": {
"self": {
"href": "/movies"
}
}
}
以上都正确吗?
谢谢
正如在您发布的 specs 中所观察到的,您可以链接 and/or 嵌入式资源:
资源的链接应作为该资源的 属性:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
"_links": {
"subtitles": {
"href": "/movies/123/subtitles"
}
}
}, {
"id": "456",
"title": "Movie title 2",
"_links": {
"subtitles": {
"href": "/movies/456/subtitles"
}
}
}
]
}
备选方案 是直接嵌入电影的字幕资源:
{
"movies" : [
{
"id" : "123",
"title" : "Movie title 1",
"_embedded" : {
"subtitles" : [{
"name" : "movie 1 subtitles"
}
]
}
}, {
"id" : "456",
"title" : "Movie title 2",
"_embedded" : {
"subtitles" : [{
"name" : "movie 2 subtitles"
}
]
}
}
]
}
“_Links”属性 必须位于 资源对象 的根目录中。 资源对象可能在根目录中,也可能在_embedded对象.
中我怀疑一些令人困惑的地方来自于将“_embedded”键指向数组。仅当您想要表示相关资源的多个实例时才这样做。
在示例中,键是 movies
,这表明您正在嵌入代表多部电影的资源对象。但是,该数组表示有多个嵌入的资源对象。每个资源对象是一部电影。
通过将密钥名称更改为 "movie",您将得到:
{
"movies": [
{
"id": "123",
"title": "Movie title 1",
},{
"id": "456",
"title": "Movie title 2",
}
],
"_embedded": {
"movie": [
{
"id": "123",
"_links": {
"href": "movies/123/subtitles"
}
},
{
"id": "456",
"_links": {
"href": "movies/456/subtitles"
}
}
]
}
"_links": {
"self": "https://example.org/movielist"
}
}
所以,现在您有一个 "movie-list" 资源对象的表示,并且您已经为 "movie-list" 中的每个项目嵌入了一堆 "movie" 资源对象。 "movie"指向的每个资源对象都有一个“_links”属性相关信息。我假设 "subtitles" link 应该是一个自我 link.
打算将其用作使用 hal 重新设计的案例研究。 @darrel millers 的回答很好,但不是很好,有些事情我认为应该澄清。这会很长。
最大的问题是上下文是什么...IE,您要返回的资源是什么。你所拥有的只是某种与电影有关的东西。就像假设这是一个搜索结果......与电影有关系是错误的方法......因为电影与搜索结果 "top level resource" 作为一个项目相关。所以它应该更像是
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
}
}
但是这种结构对于客户端构建一个 UI 来说是昂贵的,因为它必须进行 3 次调用..遵循 N+1 规则(1 用于结果集..然后 N 用于每个结果)因此诞生了_embedded,它只是超文本预取模式的 hal 实现(在 http2 中,服务器实际上可以将每个结果作为它自己的文档发送,并且客户端的缓存将 pre-filled 包含这些结果,你会' 一定需要 _embedded)。该结构看起来更像这样:
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
_embedded : {
item : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
获得 3 个资源的 1 个 http 请求非常棒。 1 个请求不是 N+1。谢谢哈尔!
那为什么是项目?好吧,搜索结果是否只包含电影……这不太可能……即使今天确实如此……您是否希望它明天只包含电影……这是非常狭窄的,这种结构是您的合同必须永远保持基本。但是您的 UI 真的很想将结果显示为电影。我添加的配置文件 link 是为了......客户端使用配置文件 link 来了解它当前正在处理的资源是什么......以及它可以使用哪些字段来构建 UI。一个体面的客户端在处理 collection 时会显示它可以显示的配置文件......而只是忽略它不能显示的配置文件(可能会记录警告)。由客户端开发人员升级他们的应用程序以支持新配置文件……不相信我吗?想一想 Web 浏览器如何解析它在 html 中不理解的标签...在您的 html 文档中添加一个 <thing-not-invented-yet></think-not-invented-yet>
并查看一个好的客户端如何工作。
你应该注意的另一件事是我不使用 self links 但规范..多年来我改变了我对此的立场。最近我默认使用 canonical 并且我只在维护目标资源的版本时使用 self 并且将嵌入的 object link 嵌入到特定版本很重要。这在我的经验中是非常罕见的。我告诉客户在它存在时跟随自己,否则在导航到嵌入项目时遵循规范。这使服务器可以完全控制它想要将客户端带到哪里。顶级资源,在这种情况下,结果仍然应该有一个自我......在这种情况下,它是有意义的,因为 soem 随机搜索可能没有规范 link......除非它是一个非常常见的关键字搜索...那么它可能应该因为其他用户可以使用相同的 url.
让我们花点时间谈谈为什么 item
作为 rel...因为这真的很重要。既然是搜索结果..为什么不让 rel 为 result
。答案很简单。result
不是 IANA link 注册表的成员 https://www.iana.org/assignments/link-relations/link-relations.xhtml,因此 result
完全无效...现在您可以 "namespace" 您的扩展名与 my:result
或 our:result
相关("namespace" 由您决定,这些只是示例)但是如果 IANA 注册表中已经存在一个非常好的扩展名,为什么还要费心呢。 .而且确实 item
。
我们来谈谈 items
与 item
(或 x:movies
与 x:movie
)。好吧 items
也不在 IANA 中..所以它必须是 x:items
但是让我们想想为什么不这样做。如果我们的结果文档以 HTML 表示,它看起来像这样(为简洁起见,忽略我丢失的 body 头部等而不是 well-formedness):
<html>
<title>Results for search XXXXX</title>
<a rel="item" href="https://host.com/url/to/movie/result/one" >A Great Movie</a>
<a rel="item" href="https://host.com/url/to/movie/result/two" >A Great Movie</a>
</html>
这是与第一个示例相同的资源(没有嵌入子资源)。仅表示为 text/html
而不是 application/hal+json
。如果我在这里迷失了你(这是大多数人真正感到困惑的地方,我能提供的最好的办法就是在 https://www.youtube.com/watch?v=u_pZBBELeEQ 上观看我对此的演讲)很明显每个目标资源的适当关系是一个单一的项目而不是一组 ITEMS。每个 link 都针对一个项目(或一部单部电影)。
HAL 有一个陷阱,将其视为 JSON,这会导致像 movies
机器可读或更好的评论那样的陈述。让我继续用例中的 HTML 表示来解释这是如何发生的。
当客户端解析此文档以查找 item
link 时,它必须解析每个 a
标记并过滤到仅存在 rel="item"
属性的标记。那是 "full table scan"..我们如何摆脱这些?我们创建一个索引。 JSON 的结构中内置了索引的概念。它是一个带有数组值的键。 index : [ {entry 1}, {entry 2} ]
。 HAL 的作者知道检索 links(在 _links 或 _embedded 中预取的那些)的最常见方法是通过关系..所以他构建了他的规范,使得 rel 被索引。秒当你看到:
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"},
item : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
知道这是真的
_links : {
self : { rel: "self", href: "https://host.com/search/with/params/XXXXX"},
item : [
{ rel:"item", href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ rel:"item", href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
]
},
因为 rel 是 LINK OBJECT 的属性而不是资源。但是 http 上的字节很昂贵(gzip 会摆脱这个)并且开发人员不喜欢冗余(另一个主题)所以当我们有 hal 时我们省略 rel 属性,因为 HAL 结构已经使 rel apparent.虽然当你的解析器遇到这个时它并不是真正的 apparent:
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"}
什么关系?您必须从 parent 节点传递它。这一直很丑陋...无论如何,所有这些都是为了表明 HAL 中通常消除了冗余。一旦消除了这种冗余,很容易将该索引键更改为复数形式 items
但要知道这意味着您说的是 link (一旦冗余被放回)将是 {rel: "items", href : "https://host.com/url/to/movie/result/one", title : "A great Movie"}
并且这显然是错误的..link 不是很多项目...只有一个。
所以在这种情况下删除冗余可能不是最好的..但它是有好处的,HAL 遵循 _links 和 _embedded 的模式,这就是我们要在搜索中做的事情结果..鉴于所有 item
links 都没有 pre-fetched 并且作为 _embedded 存在,将它们保留在 _links 中并不重要。因此它应该是这样的:
{
title : "Results for search XXXXX"
_links : {
self : { href: "https://host.com/search/with/params/XXXXX"}
},
_embedded : {
item : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
现在我们有一个很好的搜索结果,包括 2 部电影(并且将来可以在不违反合同的情况下包括更多的东西)。注意:如果您曾经使用 JUST _links 而没有使用 _embedded...您不能删除 _links 因为一些客户端依赖于它们的存在..所以最好早点想到这些东西......认为一个表现良好的客户在使用资源的 HAL 表示时应该总是在 _links 之前检查 _embedded......所以你真的要知道你的所有客户是否都是表现良好。
好的,让我们转到 x:movie
是正确关系的情况..如果顶级资源是演员,那可能会很好。所以像:
{
Name : "Paul Bettany"
_links : {
canonical : { href: "https://host.com/paul-bettany"},
"x:movie" : [
{ href : "https://host.com/url/to/movie/result/one", title : "A great Movie"},
{ href : "https://host.com/url/to/movie/result/two", title : "A Terrible Movie"},
],
"x:spouse" : { href: "", title: "Jennifer Connely"}
},
_embedded : {
"x:movie" : [
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/one"}
},
title : "a great movie",
rating : "PG",
},
{
_links : {
profile : {href : "https://host.com/result-movie"},
canonical : {href : "https://host.com/url/to/movie/result/two"}
},
title : "a terrbile movie"
rating : "G",
}
]
}
}
注意:我在顶层使用了 canoncial 而不是 self,因为 actor 是 long-lived 资源..那个 actor 将永远存在..并且 actor 没有版本。为了完整起见,我在 _links 和 _embedded 中都保留了 x:movie,但实际上我不会在 _item 中保留它们。我还将它们保留在 _links 中以显示 x:movie 的原因,以便您可以将其与 x:spouse 区分开来(在我们搜索结果的情况下,语义差异没有意义开始于)。最后,值得注意的是我嵌入了 x:movie
而不是 x:spouse
这只是为了说明它不是非此即彼的事情。您可以 pre-fetch/embed 您的用例所需的 link。事实上,我经常根据客户的身份嵌入内容。也就是说,我知道 iOS 可以显示 android 不能显示的内容。
抛开那些注释,我来这里的原因是我想明确表示您没有也不应该拥有该电影:您拥有的数据字段...仅依赖于 _embedded 中的电影数据。你说 soemthign 就像将电影中的值与 _links 或 _embedded 中的值相匹配......你不应该那样做......那没有任何意义。电影是一种资源...使用电影的 linked 资源而不是某些数据字段。您需要尽早决定什么是资源,什么是数据。我最好的提示是,如果某事物具有 link 关系..那么它就是一种资源。在我的演讲中,我使用更广泛的术语(超媒体控件)对此进行了更多详细介绍,但我不想在这里讨论。
最后一点..在超媒体应用程序中,如果您公开内部 ID 字段,您知道自己做错了什么..正如您在此处所做的那样。这应该是一个巨大的危险信号,表明出现了问题。您描述的 id 的用例是将数据字段电影与 _embedded x:movie 匹配。如前所述......你不应该这样做......并且 id 字段的存在应该让你陷入这种不良做法。
我被要求在这里回答..所以我希望这对你有所帮助。