包含(Includes)
简而言之:使用类似 ?include=field_comments.uid
的查询字符串,可以一次性包含 field_comments
引用的所有实体,以及这些实体上 uid
引用的所有实体!
JSON:API 通过允许你指定关系路径并将其包含在响应文档中,帮助你减少 HTTP 请求。如何实现呢?
获取单个资源
获取文章
假设你有一篇带有两条评论的文章,并且这两条评论的作者是同一个人。如果不使用 includes,要获取这些数据,你需要先请求 GET /jsonapi/node/article/some-random-uuid
:
{
"data": {
"type": "node--article",
"id": "some-random-uuid",
"relationships": {
"field_comments": {
"links": {
"related": {
"href": "https://my.example.com/node/article/some-random-uuid/field_comments"
}
}
}
}
}
}
获取评论
然后,你需要请求 GET /node/article/some-random-uuid/field_comments
:
{
"data": [{
"type": "comment",
"id": "one-random-uuid",
"relationships": {
"uid": {
"links": {
"related": {
"href": "https://my.example.com/comment/one-random-uuid/uid"
}
}
}
}
}, {
"type": "comment",
"id": "two-random-uuid",
"relationships": {
"uid": {
"links": {
"related": {
"href": "https://my.example.com/comment/two-random-uuid/uid"
}
}
}
}
}
}
获取用户
接着,你还需要对 /comment/one-random-uuid/uid
和 /comment/two-random-uuid/uid
发出两个请求。但在这个示例中我们知道,这两条评论的作者其实是同一个人,因此第二个请求完全没必要。
那么,includes 如何帮助我们呢?
使用 include 一次性获取所有数据
很简单!只需在原始请求 URL 上添加一个查询参数,指定你想要包含的关系字段名称,服务器就会自动查询这些数据,并将它们添加到原始响应文档中。
在我们的示例中,请求 URL 会是 GET /jsonapi/node/article/some-random-uuid?include=field_comments.uid
。换句话说,你的意思是“请把文章上的 field_comments
字段的资源对象加进来,然后再把评论引用的 uid
字段的资源对象也加进来”。这些“关系路径”可以任意长,没有限制!
服务器返回的响应文档如下:
{
"data": {
"type": "node--article",
"id": "some-random-uuid",
"relationships": {
"field_comments": {
"data": [{
"type": "comment",
"id": "one-random-uuid",
}, {
"type": "comment",
"id": "two-random-uuid",
}],
"links": {
"related": {
"href": "https://my.example.com/node/article/some-random-uuid/field_comments"
}
}
}
}
},
"included": [{
"type": "comment",
"id": "one-random-uuid",
"relationships": {
"uid": {
"data": [{
"type": "user",
"id": "another-random-uuid",
}],
"links": {
"related": {
"href": "https://my.example.com/comment/one-random-uuid/uid"
}
}
}
}
}, {
"type": "comment",
"id": "another-random-uuid",
"relationships": {
"uid": {
"data": [{
"type": "user",
"id": "one-random-uuid",
}],
"links": {
"related": {
"href": "https://my.example.com/comment/two-random-uuid/uid"
}
}
}
}
}, {
"type": "user",
"id": "another-random-uuid",
"attributes": {
"name": "c0wb0yC0d3r"
}
}]
}
是不是很酷?我们在一个请求中就得到了所有数据!注意,即使用户资源对象被引用了两次,它也只包含一次。这使响应体积更小。还要注意,现在每个关系对象中都有一个 data
键,这让你能够将包含的资源对象与引用它们的资源对象关联起来。
何时使用 include?
说到响应体积……在这个示例中,我们通过一次性获取所有资源节省了时间。但在某些情况下,包含相关资源对象可能会让响应体积过大,或导致首次字节时间(TTFB)变慢。这种情况下,发起多个并行请求可能更好。
集合与关系的 include
最后,include
查询参数同样适用于集合和关系资源!在集合上使用 include 可以节省大量请求。
集合 include 示例
获取集合 include 的请求类似于 GET /jsonapi/node/article?include=uid
。included
与 data
分开(数组而不是对象),如下所示:
{
"data": [{...}]
"included": [{
"type": "user",
"id": "another-random-uuid",
"attributes": {
"name": "c0wb0yC0d3r"
}
}]
}
文章来源:Drupal 文档。