JSON:API规范

英文原址

JSON:API版本,目前是1.0版。新版本的JSON:API将始终向后兼容使用永不删除,只添加策略。

介绍

JSON:API是关于客户机应该如何请求获取或修改资源,以及服务器应该如何响应这些请求的规范。

JSON:API旨在将请求数量和客户机与服务器之间传输的数据量最小化。这种效率是在不损害可读性、灵活性或可发现性的情况下实现的。

JSON:API需要使用JSON:API媒体类型(application/vnd.api+json)来交换数据。

约定

本文件中必须一定不能`REQUIREDSHALLSHALL NOTSHOULDSHOULD NOTsuggestMAYOPTIONAL等关键词按RFC2119 [RFC2119]解释。

内容协商

客户端责任

请求中header必须携带Content-Type: application/vnd.api+json, 表示请求数据为 JSON:API数据, 不能携带其它任何媒体类型参数。

请求中header的Accept: application/vnd.api+json 表示可以接收JSON:API数据。

客户端必须忽略响应文档的Content-Type头中接收到的application/vnd.api+json媒体类型的任何参数。

服务端责任

服务端响应JSON:API数据必须在响应中携带header Content-Type: application/vnd.api+json,表示响应数据为JSON:API数据。

如果请求没有指定Content-Type: application/vnd.api+json header的媒体类型参数,服务端必须响应一个带415 Unsupported Media Type(不支持的媒体类型)的状态码。

如果请求header 不包含Accept的媒体类型参数或其它媒体参数, 服务端必须响应一个带406 Not Acceptable(不可接受)的状态码。

注意:存在内容协商需求,以允许此规范的未来版本使用媒体类型参数进行扩展协商和版本控制

文档结构

本节描述JSON:API文档的结构,该文档由媒体类型application/vnd.api+json标识。JSON:API文档是用JavaScript对象表示法(JSON) [RFC7159]定义的。

尽管请求和响应文档都使用相同的媒体类型,但某些方面只适用于其中之一。下面列出这些差异。

除非另有说明,否则此规范定义的对象不能包含任何其他成员。客户机和服务器实现必须忽略此规范不能识别的成员。

顶层结构

一个JSON对象必须位于每个JSON:API请求和响应数据的根级。此对象定义文档的 “顶层”

文件必须至少包含下列其中一名顶层成员:

  • data: 文档的主要数据
  • errors: 错误对象数组
  • meta: 包含非标准元信息的元对象

成员dataerrors 一定不能在同一文档中共存

文档可以包含以下任何顶层成员:

  • jsonapi:描述服务器实现的对象
  • links:与主数据相关的链接对象
  • included:与主数据和/或彼此相关的资源对象数组(包含的资源)

如果文件不包含顶层data成员,included成员 必须不存

顶层links对象 可以包含以下成员:

  • self: 生成当前响应文档的链接
  • related: 主数据表示资源关系时的相关资源链接
  • 主要数据的分页链接

文档的主要数据表示请求所针对的资源或资源集合。

主要数据必须是:

  • 对于以单个资源为目标的请求: 单个资源对象,单个资源标识符对象,或者null
  • 用于定位资源集合的请求: 资源对象数组资源标识符对象数组,或一个空数组([])

例如,以下主要数据是单个资源对象:

1
2
3
4
5
6
7
8
9
10
11
12
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
// ... this article's attributes
},
"relationships": {
// ... this article's relationships
}
}
}

例如,以下主要数据是引用相同资源的单个资源标识符对象:

1
2
3
4
5
6
{
"data": {
"type": "articles",
"id": "1"
}
}

资源的逻辑集合必须表示为数组,即使它只包含一个空数组。

资源对象

资源对象出现在JSON:API文档中以表示资源。

资源对象必须至少包含以下顶级成员:

  • id
  • type

例外id当资源对象在客户端发起并表示要在服务器上创建的新资源时,不需要该成员。

此外,资源对象可以包含以下任何顶级成员:

  • attributes: 表示某些资源数据的属性对象。
  • relationships: 描述资源与其他JSON:API资源之间关系的关系对象。
  • links: 包含与资源相关的链接的链接对象。
  • meta: 包含有关无法表示为属性或关系的资源的非标准元信息的元对象。

以下是文章(即“文章”类型的资源)可能出现在文档中的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
}
}
// ...

识别(Identification)

每个资源对象 必须包含一个id成员和一个type成员。id和type成员的值必须是字符串。

在给定的API中,每个资源对象type和id对必须 识别单个唯一资源。(由服务器控制的URI集合,或作为一个服务器的多个服务器构成的API)

该type成员用于描述共享公共属性和关系的资源对象

type成员的值必须遵守与成员名称相同的约束

字段

资源对象的attributes(属性)和它的relationships(关系)中的域被统称为它的 字段

资源对象的 字段必须彼此共享一个 共同的命名空间,并不能使用type和id。换句话说,资源不能具有相同名称的属性和关系,也不能具有名称是type和id的属性或关系。

举例, 以下包括错误❌的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
{
"type": "articles",
"id": "1",
"attributes": {
"type": "literature",
"title": "Rails is Omakase"
},
"relationships": {
"title": {
}
}
}
// ...
  • 属性中type字段, 错误命名成了type
  • 属性和关系中都有了title字段

属性

attributes键的值必须是一个对象(属性对象)。属性对象(attributes)的成员表示有关其定义的资源对象的信息。

属性可以包含任何有效的JSON值。

允许涉及JSON对象和数组的复杂数据结构作为属性值。
然而,构成或包含在一个属性的任何对象必须不包含relationshipslinks字段,因为这些字段由本说明书中以供将来使用保留。

虽然有一个外键(例如author_id)通常存储在内部以及要在资源对象中表示的其他信息,但这些键不应该作为属性出现。

关系

relationships键的值必须是一个对象(关系对象)。关系对象(relationships)的成员表示从其定义到其他资源对象的资源对象的引用。

关系可能是一对一或多对。

关系对象 必须至少包含以下其中一项:

  • links:包含以下至少一项的链接对象:
    • self:关系本身的链接(“关系链接”)
      此链接允许客户端直接操纵关系。
      例如,删除author通过article关系URL将断开该人与article不删除people资源本身的关系。成功获取后,此链接将 相关资源的链接作为其主要数据返回。
    • related:相关的资源链接
  • data:资源联动
  • meta:包含有关关系的非标准元信息的元对象。

表示多对多关系的关系对象也可以在成员下包含 分页链接links,如下所述。关系对象中的任何 分页链接必须对关系数据进行分页,而不是相关资源。

相关资源链接

相关资源链接可以访问资源对象 链接 的关系。获取时,相关资源对象将作为响应的主数据返回。

例如,article的comments 关系可以指定一个链接返回评论的集合资源对象 通过检索时GET请求。

如果存在,相关资源链接必须引用有效的URL,即使该关系当前未与任何目标资源相关联。
此外,相关资源链接不得更改,因为其关系的内容会发生变化。

资源链接

复合文档中的资源链接允许客户端将所有包含的资源对象链接在一起,而无需GET通过链接访问任何URL 。

资源链接必须表示为以下之一:

  • null 为了空对一的关系。
  • 空数组([])用于空对多关系。
  • 非空 对一关系的单个资源标识符对象。
  • 非空 对多关系的资源标识符对象数组。

例如,以下文章与以下内容相关联author:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/articles/1"
}
}
// ...

该author关系包括关系本身的链接(允许客户端直接更改相关作者),用于获取资源对象的相关资源链接以及链接信息。

资源链接

links每个资源对象中的可选成员包含 与资源相关的链接。

如果存在,则此链接对象可以包含标识资源对象所表示的资源的self 链接。

1
2
3
4
5
6
7
8
9
10
11
12
// ...
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"links": {
"self": "http://example.com/articles/1"
}
}
// ...

服务器必须GET使用包含资源作为主数据的响应来响应对指定URL 的请求。

资源标识符对象

“资源标识符对象”是标识单个资源的对象。

“资源标识符对象” 必须包含type和id成员。

“资源标识符对象” 可能还包括一个meta构件,它的值是一个元包含非标准元信息对象。

复合文件

为了减少HTTP请求的数量,服务器可以允许包含相关资源和所请求的主要资源的响应。这种反应被称为“复合文件”。

在复合文档中,所有包含的资源必须包含在顶级成员中的资源对象数组included中。

复合文档需要“完全链接”,这意味着每个包含的资源必须由 同一文档中的至少一个资源标识符对象标识。这些资源标识符对象可以是主数据,也可以表示主要或包含的资源中包含的资源链接。

完全链接要求的唯一例外是当通过稀疏字段集排除否则将包含链接数据的关系字段时。

注意:完全链接可确保包含的资源与主数据(可以是资源对象或资源标识符对象)相关,也可以相互关联。

包含多个包含关系的完整示例文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "2" }
}
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2021 朝着牛逼的道路一路狂奔 All Rights Reserved.

访客数 : | 访问量 :