├── .gitignore
├── CNAME
├── README.md
├── _config.yml
├── _includes
├── footer.html
├── header.html
└── status.md
├── _layouts
└── page.html
├── about
└── index.md
├── design
└── jsonapi.sketch
│ ├── Data
│ ├── metadata
│ └── version
├── examples
└── index.md
├── extending
└── index.md
├── faq
└── index.md
├── format
└── index.md
├── images
├── blockquote.gif
├── forkme_right_red_aa0000.png
├── jsonapi@0.7x.png
└── jsonapi@1.3x.png
├── index.md
├── javascripts
├── all.js
└── highlight.pack.js
├── recommendation
└── index.md
├── schema
├── status
└── index.md
└── stylesheets
├── all.css
├── highlight.css
└── normalize.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile ~/.gitignore_global
6 |
7 | Gemfile.lock
8 |
9 | # Ignore bundler config
10 | /.bundle
11 |
12 | # Ignore the build directory
13 | /public
14 |
15 | # Ignore Sass' cache
16 | /.sass-cache
17 |
18 | /bin
19 | /b
20 | .rbx
21 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | jsonapi.org.cn
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JSON API:用 JSON 构建 API 的标准指南中文版
2 | ========
3 |
4 | 本项目是网站 [http://jsonapi.org.cn](http://jsonapi.org.cn) 的源码。
5 |
6 | 英文官方网址:[http://jsonapi.org](http://jsonapi.org)。
7 |
8 | 资源
9 | ---------
10 |
11 | * IRC channel: #jsonapi on freenode.net
12 | * Twitter: @jsonapi
13 |
14 | 翻译
15 | ---------
16 |
17 | * [justjavac(迷渡)](http://github.com/justjavac)
18 | * [bornkiller](http://github.com/bornkiller)
19 |
20 |
21 | 现状 Status
22 | ------
23 |
24 | 本标准是目前正在开发中。如需贡献:
25 |
26 | 1. 获取所有依赖:
27 |
28 | `$ bundle`
29 |
30 | 1. 搭建本地站点:
31 |
32 | `$ bundle exec rake preview:browser`
33 |
34 | 1. 编辑 Markdown 文件。
35 | 1. 提交更改。
36 | 1. 提交 Pull Request。
37 |
38 | 当更改被合并到 `gh-pages` 分支,本站点将由 [GitHub Pages](http://pages.github.com) 自动构建。
39 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | url: http://jsonapi.org.cn
2 | safe: true
3 | lsi: false
4 | source: .
5 | destination: ./public
6 | exclude:
7 | - README.md
8 | - CNAME
9 | - .gitignore
10 | - ./public
11 | - ./stylesheets/*.scss
12 | markdown: redcarpet
13 | pygments: false
14 | port: 9876
15 |
16 | navigation:
17 | - title: 概述
18 | url: /
19 | - title: 格式
20 | url: /format/
21 | - title: 扩展
22 | url: /extending/
23 | - title: 示例
24 | url: /examples/
25 | - title: FAQ
26 | url: /faq/
27 | - title: 关于
28 | url: /about/
29 |
--------------------------------------------------------------------------------
/_includes/footer.html:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/_includes/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/_includes/status.md:
--------------------------------------------------------------------------------
1 | ## 现状
2 |
3 | **本文档是一个正在进展的工作**,在具体实现过程中将会有所改变。详细信息请查看[现状](/status)页面。
4 |
--------------------------------------------------------------------------------
/_layouts/page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | JSON API :: {{ page..title }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {% include header.html %}
23 |
24 |
25 |
26 | {% if page.show_masthead %}
27 |
31 | {% else %}
32 | {{ page.title }}
33 | {% endif %}
34 | {{ content }}
35 |
36 |
37 | {% include footer.html %}
38 |
39 |
40 |
--------------------------------------------------------------------------------
/about/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: About
4 | ---
5 |
6 | ## 频道
7 |
8 | * [@jsonapi](http://twitter.com/jsonapi) on Twitter
9 | * _#jsonapi_ channel on [Freenode IRC](http://freenode.net)
10 | * [jsonapi Google group](https://groups.google.com/forum/?fromgroups#!forum/jsonapi)
11 |
12 | ## 编辑者
13 |
14 | 该规范的两个主要编辑者:
15 |
16 | - Steve Klabnik:twitter [@steveklabnik](http://twitter.com/steveklabnik)
17 | - Yehuda Katz:twitter [@wycats](http://twitter.com/wycats)
18 |
19 | 翻译者:
20 |
21 | - [justjavac](http://github.com/justjavac)
22 | - [bornkiller](http://github.com/bornkiller)
23 |
24 | > 然而,对于 Web 来说,最重要的是这种关注点的分离允许组件独立地进化,
25 | > 从而支持多个组织领域的 Internet 规模的需求。
26 | >
27 | > —— Roy Fielding 《架构风格与基于网络的软件架构设计》 [第五章](http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)
28 |
29 | (译注:此书第五章介绍了 RESTful 风格的网络架构 by [@justjavac](http://weibo.com/justjavac))
30 |
31 | Steve Klabnik 主要是服务器端,而 Yehuda Katz 则是客户端。
32 | 而我们两者都关注,但我们要确保两边都有一个捍卫者。
33 |
34 | ## 历史
35 |
36 | JSON API 来自 JSON 的数据传输,它被隐式地定义在 [Ember](http://emberjs.com/) 的 REST 风格数据适配器。
37 |
38 | 一般来说,Ember Data 被设计用来实现这样的目的:消除哪些为不同应用程序与服务器之间通信而写的特殊代码,
39 | 而是用 REST 风格数据适配器将它们转换成统一的方式。
40 |
41 | 一些服务器,比如 Firebase、Parse 和 CouchDB 已经定义了和客户端通信的精确的协议,以便适合 Ember 数据。
42 | 相比之下,用 Rails、Node.js、 Django 编写的服务器端程序,倾向于使用 REST 风格,
43 | 但对于客户端程序,却没有多少是使用 REST 风格的。
44 |
45 | Ember Data 的 REST Adapter 隐式定义了一个协议,
46 | 服务器需要实现此协议,用来为客户端程序提供有所的资源。
47 | [ActiveModel::Serializers][1] 是 Rails 的一个库,并实现了 Ember
48 | Data 所期望的序列化格式。
49 |
50 | [1]: https://github.com/rails-api/active_model_serializers
51 |
52 | Ember Data 关于记录的创建、更新和删除,
53 | 已经成为 Rails、Django 和 Node.js 开发者广泛使用的约定。
54 |
55 | 媒体类型的目标是追求均衡:
56 |
57 | * 一个通用的媒体类型,可以在非常广泛的场景中使用,包括常用的关系类型
58 | * 对现有服务器端框架最佳实践类似(可读性与可调试性)
59 | * 易于在服务器端实现
60 | * 易于在客户端实现
61 |
62 | 该类媒体仍然是一个进展中的工作,我们是非常开放的反馈和建议改进。
63 | 就是说,该规范的实现工作已经开始,我们的价值在于提供更好的雾件(vaporware)。
64 |
--------------------------------------------------------------------------------
/design/jsonapi.sketch/Data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justjavac/json-api-zh_CN/f33dd60e301338a59829ee2a2bfdabdff7ddc6e7/design/jsonapi.sketch/Data
--------------------------------------------------------------------------------
/design/jsonapi.sketch/metadata:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | app
6 | com.bohemiancoding.sketch3
7 | build
8 | 7597
9 | commit
10 | 463d76bf23dc890960e184011f80b3dacac572b9
11 | fonts
12 |
13 | AmericanTypewriter
14 |
15 | length
16 | 8333
17 | version
18 | 36
19 |
20 |
21 |
--------------------------------------------------------------------------------
/design/jsonapi.sketch/version:
--------------------------------------------------------------------------------
1 | 36
--------------------------------------------------------------------------------
/examples/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Examples
4 | ---
5 |
6 | Examples are excellent learning aids. The following projects implementing JSON
7 | API are divided into server- and client-side. The server-side is further
8 | divided by implementation language. If you'd like your project listed, [send a
9 | Pull Request](https://github.com/json-api/json-api).
10 |
11 | ## Client
12 |
13 | ### JavaScript
14 |
15 | * [ember-data](https://github.com/emberjs/data) is one of the original exemplar
16 | implementations. There is a [custom adapter](https://github.com/daliwali/ember-json-api) to support json-api.
17 |
18 | * [backbone-jsonapi](https://github.com/guillaumervls/backbone-jsonapi) is a Backbone adapter for JSON API. Supports fetching Models & Collections from a JSON API source.
19 |
20 | ### iOS
21 |
22 | * [jsonapi-ios](https://github.com/joshdholtz/jsonapi-ios) is a library for loading data from a JSON API datasource. Parses JSON API data into models with support for auto-linking of resources and custom model classes.
23 |
24 | ## Server
25 |
26 | ### PHP
27 |
28 | * [FriendsOfSymfony / FOSRestBundle](https://github.com/FriendsOfSymfony/FOSRestBundle/issues/452)
29 |
30 | ### Node.js
31 |
32 | * [Fortune.js](http://fortunejs.com) is a framework built to implement json-api.
33 |
34 | ### Ruby
35 |
36 | * [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers)
37 | is one of the original exemplar implementations, but is slightly out of date at
38 | the moment.
39 |
40 | * [JsonApiClient](https://github.com/chingor13/json_api_client) attempts to give you a query building framework that is easy to understand (similar to ActiveRecord scopes)
41 |
42 | * [The rabl wiki](https://github.com/nesquena/rabl/wiki/Conforming-to-jsonapi.org-format)
43 | has a page describing how to emit conformant JSON.
44 |
45 | * [RestPack::Serializer](https://github.com/RestPack/restpack_serializer) implements the read elements of json-api. It also supports paging and side-loading.
46 |
47 | * [Oat](https://github.com/ismasan/oat#adapters) ships with a JSON API adapter.
48 |
49 | ### Python
50 |
51 | * [Hyp](https://github.com/kalasjocke/hyp) is a library for creating json-api responses.
52 |
53 | * [SQLAlchemy-JSONAPI](https://github.com/coltonprovias/sqlalchemy-jsonapi) provides JSON API serialization for SQLAlchemy models.
54 |
55 | ## Messages
56 |
57 | * [RestPack::Serializer provides examples](http://restpack-serializer-sample.herokuapp.com/) which demonstrate sample responses.
58 |
59 | ## Related Tools
60 |
61 | ### Ruby
62 |
63 | * [json-patch](https://github.com/guillec/json-patch) implementation of JSON Patch (rfc6902)
64 |
65 | * [hana](https://github.com/tenderlove/hana) implementation of the JSON Patch and JSON pointer spec
66 |
67 | ### Node.js
68 |
69 | * [json-patch](https://www.npmjs.org/package/json-patch) implementation of JSON Patch (rfc6902)
70 |
--------------------------------------------------------------------------------
/extending/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: 扩展
4 | ---
5 |
6 | {% include status.md %}
7 |
8 | ## 扩展
9 |
10 | 如果你想扩展 JSON API,你应该遵循 [RFC 6906](http://tools.ietf.org/html/rfc6906) 定义的 profile。
11 | 另请参阅 [Mark Nottingham 写的这篇文章](http://www.mnot.net/blog/2012/04/17/profiles)。
12 |
13 | `meta` 部分定义了 profile 的链接。
14 |
15 | **注意**,在 RFC 规范中,profile:
16 |
17 | > 不改变资源表示的语义定义本身,但让客户了解更多的语义(约束、规范、扩展)相关联的资源表示形式,
18 | > 还有这些定义的媒体类型和可能的其他机制。
19 |
20 | ## 示例
21 |
22 | 例如,假设你想让你的 API 支持不同的分页设计,如基于游标。
23 | 你会制作某种信息页面在你的网站上,如 `http://api.example.com/profile`,
24 | 然后会响应中包含 `meta` 键:
25 |
26 | ```text
27 | GET http://api.example.com/
28 | ```
29 |
30 | ```json
31 | {
32 | "meta": {
33 | "profile": "http://api.example.com/profile"
34 | },
35 |
36 | "posts": [{
37 | // 一份单独的文档
38 | }]
39 | }
40 | ```
41 |
42 | That document will de-reference to explain your link relations:
43 |
44 | 这份文档将解释链接之间的关系:
45 |
46 | ```text
47 | GET http://api.example.com/profile HTTP/1.1
48 | ```
49 |
50 | ```text
51 | HTTP/1.1 200 OK
52 | Content-Type: text/plain
53 | ```
54 |
55 | The Example.com API Profile
56 | ===========================
57 |
58 | Example.com API 使用基于游标的分页。
59 | 它是这样工作的:
60 | 在想要的 `meta` 部分,它将返回一个 `cursors` 的关系(relation),
61 | 其中包括 `after`,`before` 和 `limit`,用来描述该游标。
62 | 您可以使用 `href` 给出的 URI 模板来生成分页的 URIs。
63 |
64 | ```json
65 | "meta": {
66 | "cursors": {
67 | "after": "abcd1234",
68 | "before": "wxyz0987",
69 | "limit": 25,
70 | "href": "https://api.example.com/whatever{?after,before,limit}"
71 | }
72 | }
73 | ```
74 |
--------------------------------------------------------------------------------
/faq/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: FAQ
4 | ---
5 |
6 | ### 为什么 JSON API 还没有发布版?
7 |
8 | 一旦 JSON API 发布稳定版,它将保持向后兼容,它将遵守**永不删除,只是添加**的开发策略。
9 | [#46](https://github.com/json-api/json-api/issues/46)
10 |
11 | ### 为什么不使用 HAL 规范?
12 |
13 | 有几个原因:
14 |
15 | * HAL 递归嵌套子文档,而 JSON API 在顶层采用扁平化对象结构。意味着不同的对象引用相同的 “people”(例如,posts和comments的author)时,这种规范能够保证每个person document仅存在一个有效实例。
16 |
17 | * 相似的,JSON API使用IDs做链接,使从复合响应中缓存文档成为可能,仅当本地不存在对应文档,才会发出后续请求。如果幸运,甚至可以完全无需HTTP请求。
18 |
19 | * HAL是序列化格式,但完全未定义文档更新操作。JSON API则仔细考虑如何更新已存在文档(依赖PATCH和JSON PATCH),以及更新操作与GET请求返回复合文档交互方式。同时定义如何创建,删除文档,以及更新操作的200,204响应。
20 |
21 | 简单来说,JSON API尝试格式化相似的,特殊的client-server通讯接口,使用JSON作为数据交换格式。专注于使用成熟的客户端来调用相关API,客户端能够缓存已经获取到的文档,避免再次请求已缓存信息。
22 |
23 | JSON API从大量实际项目所使用的库中抽象而出。同时定义请求/响应(HAL未定义),以及对应数据交互格式。
24 |
25 | ### 如何获取资源可能的行为?
26 | 你应该使用OPTIONS HTTP方法来获取当前特定资源的行为。OPTIONS请求返回方法的语义遵循JSON API标准。
27 |
28 | 举例来说,如果`"GET,POST"`是URL OPTIONS请求的响应,那么就可以获取该资源信息,以及创建新资源。
29 |
30 | 如果你想知道特定资源属性作用,你不得不使用应用级别的描述来定义属性的含义与功能,并使用错误响应通知用户。这个特性依旧在讨论中,尚未加入最终标准。[discussion](https://github.com/json-api/json-api/issues/7).
31 |
32 | ### 有没有JSON 规范来定义JSON API?
33 |
34 | 当然,你可以在[http://jsonapi.org/schema](http://jsonapi.org/schema)找到JSON规范定义。注意这个规范并不完美。 因为JSON文档可能会通过规范检查,但并不意味着是合适的JSON API文档。规范只是为了常规性排错检查。
35 |
36 | 可以在[http://json-schema.org](http://json-schema.org)找到更多关于JSON 规范格式的信息。
37 |
38 | ### 为什么资源集合作为数组返回,而不是ID索引集合?
39 |
40 | JSON数组是自然排序,而集合需要元数据进行成员排序。因此,默认情况下,数组能够实现更自然的排序或者特殊方式排序。
41 |
42 | 除此之外,JSON API 允许返回不包含IDs的只读资源,与IDs索引集合方式不兼容。
43 |
44 | ### 为什么关联资源嵌套在复合文档的 `linked` 对象中?
45 |
46 | 主要资源应该相互独立,因为他们的顺序和数量通常比较重要。通过多种方式,分离主要资源和关联资源是必要的,因为主要资源可能会有相同类型的关联资源(e.g. the "parents" of a "person")。关联资源嵌套在 `linked` 中能够防止可能的冲突。
47 |
--------------------------------------------------------------------------------
/format/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: "格式"
4 | ---
5 |
6 | {% include status.md %}
7 |
8 | ## 介绍
9 |
10 | JSON API 是数据交互规范,用以定义客户端如何获取与修改资源,以及服务器如何响应对应请求。
11 |
12 | JSON API设计用来最小化请求的数量,以及客户端与服务器间传输的数据量。在高效实现的同时,无需牺牲可读性、灵活性和可发现性。
13 |
14 | JSON API需要使用JSON API媒体类型([`application/vnd.api+json`](http://www.iana.org/assignments/media-types/application/vnd.api+json)) 进行数据交互。
15 |
16 | JSON API服务器支持通过GET方法获取资源。而且必须独立实现HTTP POST, PUT和DELETE方法的请求响应,以支持资源的创建、更新和删除。
17 |
18 | JSON API服务器也可以选择性支持HTTP PATCH方法 [[RFC5789](http://tools.ietf.org/html/rfc5789)]和JSON Patch格式 [[RFC6902](http://tools.ietf.org/html/rfc6902)],进行资源修改。JSON Patch支持是可行的,因为理论上来说,JSON API通过单一JSON 文档,反映域下的所有资源,并将JSON文档作为资源操作介质。在文档顶层,依据资源类型分组。每个资源都通过文档下的唯一路径辨识。
19 |
20 | ## 规则约定
21 |
22 | 文档中的关键字, "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
23 | "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" 依据RFC 2119
24 | [[RFC2119](http://tools.ietf.org/html/rfc2119)]规范解释。
25 |
26 | ##内容约定
27 |
28 | ###客户端职责
29 |
30 | 客户端必须在包含`Content-Type: application/vnd.api+json`头并且不包含媒体类型参数的请求文档中发送所有JSON API数据。
31 |
32 | 在`Accept`头中包含JSON API媒体类型并且不包含媒体类型参数的客户端必须在`Accept`头中指定媒体类型至少一次。
33 |
34 | 客户端必须忽略任何从响应文档的`Content-Type`头中获取的`application/vnd.api+json`媒体类型参数。
35 |
36 | ###服务器职责
37 |
38 | 服务器必须在包含`Content-Type: application/vnd.api+json`头并且不包含媒体类型参数的请求文档中发送所有JSON API数据。
39 |
40 | 如果接收到一个用任何媒体类型参数指定`Content-Type: application/vnd.api+json`头的请求,服务器必须返回一个`415 Unsupported Media Type`状态码响应。
41 |
42 | 如果接收到一个在`Accept`头中包含任何JSON API媒体类型并且所有实体都以媒体类型参数更改的请求,服务器必须返回一个`406 Not Acceptable`状态码响应。
43 |
44 | ## 文档结构
45 |
46 | 这一章节描述JSON API文档结构,通过媒体类型[`application/vnd.api+json`](http://www.iana.org/assignments/media-types/application/vnd.api+json)标示。JSON API文档使用javascript 对象(JSON)[[RFC4627](http://tools.ietf.org/html/rfc4627)]定义。
47 |
48 | 尽管同种媒体类型用以请求和响应文档,但某些特性只适用于其中一种。差异在下面呈现。
49 |
50 | 除非另有说明,根据本规范定义的对象都不应该包含任何其他键。客户端和服务器实现必须忽略本规范未指定的键。
51 |
52 | ### Top Level
53 |
54 | JSON 对象必须位于每个JSON API文档的根级。这个对象定义文档的“top level”。
55 |
56 | 文档必须包含以下至少一种top-level键:
57 |
58 | * `data`: 文档的”primary data”.
59 | * `errors`: [错误对象](http://jsonapi.org/format/#errors)列表.
60 | * `meta`: 包含非标准元信息的[元对象](http://jsonapi.org/format/#document-meta).
61 |
62 | `data`键和`errors`键不能再一个文档中同时存在。
63 |
64 | 文档可能包含以下任何top-level键:
65 |
66 | * `jsonapi`: 描述服务器实现的对象.
67 | * `links`: 与primary data相关的[链接对象](http://jsonapi.org/format/#document-links).
68 | * `include`: 与primary data或其他资源相关的资源对象("included resources")列表.
69 |
70 | 如果文档不包含top-level `data`键,`included`键也不应该出现。
71 |
72 | 文档的top-level [链接对象](http://jsonapi.org/format/#document-links)可能包含以下键:
73 |
74 | * `self`: 生成当前响应文档的[链接](http://jsonapi.org/format/#document-links).
75 | * `related`: 当primary data代表资源关系时,表示[相关资源链接](http://jsonapi.org/format/#document-resource-object-related-resource-links).
76 | * Primary data的[分页](http://jsonapi.org/format/#fetching-pagination)链接.
77 |
78 | 文档中的“primary data”代表一个请求所要求的资源或资源集合。
79 |
80 | Primary data 必须是以下列举的一种:
81 |
82 | * 如果请求要求单一资源,应该是一个单一[资源对象](http://jsonapi.org/format/#document-resource-objects),或一个单一[资源标识符](http://jsonapi.org/format/#document-resource-identifier-objects),或`null`.
83 | * 如果请求要求资源集合,应该是一个[资源对象](http://jsonapi.org/format/#document-resource-objects)列表,或一个空列表(`[]`).
84 |
85 | 例如,以下primary data表示一个单一资源对象:
86 |
87 | ```javascript
88 | {
89 | "data": {
90 | "type": "articles",
91 | "id": "1",
92 | "attributes": {
93 | // ... this article's attributes
94 | },
95 | "relationships": {
96 | // ... this article's relationships
97 | }
98 | }
99 | }
100 | ```
101 |
102 | 以下primary data表示一个指向同样资源的单一[资源标识符](http://jsonapi.org/format/#document-resource-identifier-objects):
103 |
104 | ```javascript
105 | {
106 | "data": {
107 | "type": "articles",
108 | "id": "1"
109 | }
110 | }
111 | ```
112 |
113 | 即使只包含一个元素或为空,资源的一个逻辑集合也必须表示为一个列表。
114 |
115 | ### 资源对象
116 |
117 | JSON API 文档中的"Resuorce objects"代表资源。
118 |
119 | 一个资源对象必须至少包含以下top-level键:
120 |
121 | * `id`
122 | * `type'
123 |
124 | 例外:当资源对象来自客户端并且代表一个将要在服务器创建的新资源时,`id`键不是必须的。
125 |
126 | 此外,资源对象可能包含以下top-level键:
127 |
128 | * 'attribute': [属性对象](http://jsonapi.org/format/#document-resource-object-attributes)代表资源的某些数据.
129 | * `relationshiops`: [关联对象](http://jsonapi.org/format/#document-resource-object-relationships)描述该资源与其他JSON API资源之间的关系.
130 | * `links`: [链接资源](http://jsonapi.org/format/#document-links)包含与资源相关的链接.
131 | * `meta`: [元数据资源](http://jsonapi.org/format/#document-meta)包含与资源对象相关的非标准元信息,这些信息不能被作为属性或关联对象表示.
132 |
133 | 一篇文献(即一个"文献"类型的资源)在文档中这样表示:
134 |
135 | ```javascript
136 | // ...
137 | {
138 | "type": "articles",
139 | "id": "1",
140 | "attributes": {
141 | "title": "Rails is Omakase"
142 | },
143 | "relationships": {
144 | "author": {
145 | "links": {
146 | "self": "/articles/1/relationships/author",
147 | "related": "/articles/1/author"
148 | },
149 | "data": { "type": "people", "id": "9" }
150 | }
151 | }
152 | }
153 | // ...
154 | ```
155 |
156 | #### 标识符
157 |
158 | 每个[资源对象](http://jsonapi.org/format/#document-resource-objects)包含一个`id`键和一个`type`键。`id`键和`type`键的值必须是字符串类型。
159 |
160 | 对于每一个既定API,每个资源对象的`type`和`id`对必须定义一个单独且唯一的资源。(由一个或多个但行为表现为一个服务器的服务器控制的URI集合构成一个API。)
161 |
162 | `type`键用于描述共享相同属性和关联的[资源对象](http://jsonapi.org/format/#document-resource-objects)。
163 |
164 | `type`键的值必须与遵循[键名称](http://jsonapi.org/format/#document-member-names)相同的约束条件。
165 |
166 | ####字段
167 |
168 | 资源对象的[属性](http://jsonapi.org/format/#document-resource-object-attributes)和[关联](http://jsonapi.org/format/#document-resource-object-relationships)被统称为"[fields](http://jsonapi.org/format/#document-resource-object-fields)"。
169 |
170 | 一个[资源对象](http://jsonapi.org/format/#document-resource-objects)的所有字段必须与`type`和`id`在同一命名空间中。即一个资源不能拥有名字相同的属性与关联,也不能拥有被命名为`type`或`id`的属性和关联。
171 |
172 | ####属性
173 |
174 | `attribute`键的值必须是一个对象(一个"attributes object")。属性对象的键("attributes")代表与[资源对象](http://jsonapi.org/format/#document-resource-objects)中定义的与其有关的信息。
175 |
176 | 属性可以包含任何合法JSON值。
177 |
178 | JSON对象和列表涉及的复杂数据结构可以作为属性的值。但是一个组成或被包含于属性中的对象不能包含`relationships`或`links`键,因为这些键为此规范未来的用途所预留。
179 |
180 | 虽然一些has-one关系的外键(例如author_id)被在内部与其他将要在资源对象中表达的信息一起储存,但是这些键不能作为属性出现。
181 |
182 | ####关联
183 |
184 | `relationships`键的值必须是一个对象("relationships object")。
185 |
186 | 关联对象("relationships")的键表示在[资源对象](http://jsonapi.org/format/#document-resource-objects)中定义的与其相关的其他资源对象。
187 |
188 | 关联可以是单对象关联或多对象关联。
189 |
190 | 一个"relationship object"必须包含以下至少一种键:
191 |
192 | * `links`: 一个[链接对象](http://jsonapi.org/format/#document-links)至少包含以下一种键:
193 | + `self`: 指向关联本身的链接("relationship link")。此链接允许客户端直接修改关联。例如,通过一个`articale`的关联URL移除一个`author`将会解除一个人与`article`的关系而不需要删除这个`people`资源本身。获取成功后,这个链接将返回一个相关资源之间的[连接](http://jsonapi.org/format/#document-resource-object-linkage),将其作为primary data。(见 [获取关联](http://jsonapi.org/format/#fetching-relationships)).
194 | + `related`: [相关资源链接](http://jsonapi.org/format/#document-resource-object-related-resource-links).
195 | * `data`: [资源连接](http://jsonapi.org/format/#document-resource-object-linkage).
196 | * `meta`: 包含关于此关联的非标准元信息的[元对象](http://jsonapi.org/format/#document-meta).
197 |
198 | 一个代表多对象关联的关联对象可能在`links`键下也包含[分页](http://jsonapi.org/format/#fetching-pagination)链接,如后文描述。
199 |
200 | ####相关资源链接
201 |
202 | "related resource link"提供 [关联](http://jsonapi.org/format/#document-resource-object-relationships)中[链接](http://jsonapi.org/format/#document-links)的[资源对象](http://jsonapi.org/format/#document-resource-objects)的权限。当获取成功后,相关资源对象将作为响应的primary data返回。
203 |
204 | 例如,通过一个`GET`请求检索时,一篇`article`的`comments`关联可以指定一个返回评论集合[资源对象](http://jsonapi.org/format/#document-resource-objects)的[链接](http://jsonapi.org/format/#document-links)。
205 |
206 | 当一个相关资源链接出现时,它必须指向一个合法URL, 即使关联当前不予任何目标资源相关。此外,一个相关资源链接不能因为它的关联内容更改而更改。
207 |
208 | ####资源连接
209 |
210 | [复合文档](http://jsonapi.org/format/#document-compound-documents)中的资源连接允许客户端将所有包含的[资源对象](http://jsonapi.org/format/#document-resource-objects)链接在一起,而不需要通过[链接](http://jsonapi.org/format/#document-links)`GET`任何URL。
211 |
212 | 资源连接必须通过以下一种形式表述:
213 |
214 | * 空的单对象关联用`null`表示.
215 | * 空的多对象关联用一个空列表(`[]`)表示.
216 | * 非空单对象关联用一个[资源标识符对象](http://jsonapi.org/format/#document-resource-identifier-objects)表示.
217 | * 非空多对象关联用一个[资源标识符对象](http://jsonapi.org/format/#document-resource-identifier-objects)列表表示.
218 |
219 | 例如,以下文献与`author`相关:
220 |
221 | ```javascripts
222 | // ...
223 | {
224 | "type": "articles",
225 | "id": "1",
226 | "attributes": {
227 | "title": "Rails is Omakase"
228 | },
229 | "relationships": {
230 | "author": {
231 | "links": {
232 | "self": "http://example.com/articles/1/relationships/author",
233 | "related": "http://example.com/articles/1/author"
234 | },
235 | "data": { "type": "people", "id": "9" }
236 | }
237 | },
238 | "links": {
239 | "self": "http://example.com/articles/1"
240 | }
241 | }
242 | // ...
243 | ```
244 |
245 | `author`关联包含一个指向关联本身的链接(这允许客户端直接更改相关作者)、一个用于获取资源对象的相关资源链接和一个连接信息。
246 |
247 | ####资源链接
248 |
249 | [资源对象](http://jsonapi.org/format/#document-resource-objects)中的可选的`links`键包含与资源相关的[链接](http://jsonapi.org/format/#document-links)。
250 |
251 | 当链接对象存在时,它可能包含指向定义了资源对象所表示的资源的`self`[链接](http://jsonapi.org/format/#document-links)。
252 |
253 | ```javascript
254 | // ...
255 | {
256 | "type": "articles",
257 | "id": "1",
258 | "attributes": {
259 | "title": "Rails is Omakase"
260 | },
261 | "links": {
262 | "self": "http://example.com/articles/1"
263 | }
264 | }
265 | // ...
266 | ```
267 |
268 | 服务器收到一个特定URL的`GET`请求后,必须回复将此资源作为promary data的响应。
269 |
270 | ###资源标识符对象
271 |
272 | “resource identifier object”是一个定义单独资源的对象。
273 |
274 | “resource identifier object”必须包含`type`和`id`键。
275 |
276 | “resource identifier object”也可能包含`meta`键,它的值是包含非标准元信息的[meta](http://jsonapi.org/format/#document-meta)对象。
277 |
278 | ###复合文档
279 |
280 | 为了减少HTTP请求的数量,服务器可能允许包含相关资源与所请求的主要资源的响应。这种响应被称作"compound documents"。
281 |
282 | 在复合文档中,所有包含的资源必须以一个top-level中的`included`键中的[资源对象](http://jsonapi.org/format/#document-resource-objects)列表表示。
283 |
284 | 复合文档要求 “full linkage”,这意味着所有包含其中的资源必须至少被一个在同一文档中的[资源标识符对象](http://jsonapi.org/format/#document-resource-identifier-objects)定义。这些资源标识符对象可以是primary data或与主要或相关资源包含在一起的资源连接。
285 |
286 | 全连接要求唯一的例外是当包含连接数据的关联字段被通过稀疏字段集合排除时。
287 |
288 | 一个含有多个包含关联的复杂示例文档如下所示:
289 |
290 | ```javascript
291 | {
292 | "data": [{
293 | "type": "articles",
294 | "id": "1",
295 | "attributes": {
296 | "title": "JSON API paints my bikeshed!"
297 | },
298 | "links": {
299 | "self": "http://example.com/articles/1"
300 | },
301 | "relationships": {
302 | "author": {
303 | "links": {
304 | "self": "http://example.com/articles/1/relationships/author",
305 | "related": "http://example.com/articles/1/author"
306 | },
307 | "data": { "type": "people", "id": "9" }
308 | },
309 | "comments": {
310 | "links": {
311 | "self": "http://example.com/articles/1/relationships/comments",
312 | "related": "http://example.com/articles/1/comments"
313 | },
314 | "data": [
315 | { "type": "comments", "id": "5" },
316 | { "type": "comments", "id": "12" }
317 | ]
318 | }
319 | }
320 | }],
321 | "included": [{
322 | "type": "people",
323 | "id": "9",
324 | "attributes": {
325 | "first-name": "Dan",
326 | "last-name": "Gebhardt",
327 | "twitter": "dgeb"
328 | },
329 | "links": {
330 | "self": "http://example.com/people/9"
331 | }
332 | }, {
333 | "type": "comments",
334 | "id": "5",
335 | "attributes": {
336 | "body": "First!"
337 | },
338 | "relationships": {
339 | "author": {
340 | "data": { "type": "people", "id": "2" }
341 | }
342 | },
343 | "links": {
344 | "self": "http://example.com/comments/5"
345 | }
346 | }, {
347 | "type": "comments",
348 | "id": "12",
349 | "attributes": {
350 | "body": "I like XML better"
351 | },
352 | "relationships": {
353 | "author": {
354 | "data": { "type": "people", "id": "9" }
355 | }
356 | },
357 | "links": {
358 | "self": "http://example.com/comments/12"
359 | }
360 | }]
361 | }
362 | ```
363 |
364 | 对于每个`typy`和`id`对,一个[复合文档](http://jsonapi.org/format/#document-compound-documents)不能包含超过一个[资源对象](http://jsonapi.org/format/#document-resource-objects)。
365 |
366 | ###元信息
367 |
368 | 在指定位置,`meta`键可用于包含非标准元信息。每个`meta`键的值必须是一个对象("meta object")。
369 |
370 | 任何键都可以包含在`meta`对象中。
371 |
372 | 例如:
373 |
374 | ```javascript
375 | {
376 | "meta": {
377 | "copyright": "Copyright 2015 Example Corp.",
378 | "authors": [
379 | "Yehuda Katz",
380 | "Steve Klabnik",
381 | "Dan Gebhardt",
382 | "Tyler Kellen"
383 | ]
384 | },
385 | "data": {
386 | // ...
387 | }
388 | }
389 | ```
390 |
391 | ###链接
392 |
393 | 在指定位置,`links`键可用于表示链接。每个`links`键的值必须是一个对象("links object")。
394 |
395 | 每个链接对象的键是一个"link"。一个链接必须是以下形其中之一:
396 |
397 | * 一个包含链接的URL的字符串.
398 | * 一个可以包含以下键的对象("link object"):
399 | - `href`: 一个包含此链接URL的字符串
400 | - `meta`: 包含与此链接相关非标准元信息的元对象.
401 |
402 | 以下`self`链接是一个简单的URL:
403 |
404 | ```javascript
405 | "links": {
406 | "self": "http://example.com/posts"
407 | }
408 | ```
409 |
410 | 以下`related`链接包含一个URL和与资源集合相关的元信息:
411 |
412 | ```javascript
413 | "links": {
414 | "related": {
415 | "href": "http://example.com/articles/1/comments",
416 | "meta": {
417 | "count": 10
418 | }
419 | }
420 | }
421 | ```
422 |
423 | ###JSON API 对象
424 |
425 | 一个JSON API文档可能在top level `jsonapi`键下包含与其实现相关的信息。如果它出现,`jsonapi`键的值必须是一个对象("jsonapi object")。jsonapi对象可能包含`version`键,其值是一个表明被支持的最高JSON API版本的字符串。此对象也可能包含一个`meta`键,其值是一个包含非标准元信息的[meta](http://jsonapi.org/format/#document-meta)对象。
426 |
427 | ```javascript
428 | {
429 | "jsonapi": {
430 | "version": "1.0"
431 | }
432 | }
433 | ```
434 |
435 | 如果`version`键未出现,客户端则假设服务器是遵循此规范最新版本1.0。
436 |
437 | ####键名称
438 |
439 | 一个JSON API文档中使用的键名称对于客户端和服务器来说必须是大小写敏感的,并且必须满足以下规则:
440 |
441 | * 键名称必须包含至少一个字符.
442 | * 键名称必须只包含以下列举的合法字符.
443 | * 键名称必须以以下定义的“globally allowed character”开始和结束.
444 |
445 | 为了确保键名称能便利地映射到URL,推荐使用[RFC 3986](https://tools.ietf.org/html/rfc3986#page-13)中指定的非保留URL安全字符。
446 |
447 | ####合法字符
448 |
449 | 以下“globally allowed characters”可以被用于键名称中:
450 |
451 | * U+0061 to U+007A, “a-z”
452 | * U+0041 to U+005A, “A-Z”
453 | * U+0030 to U+0039, “0-9”
454 | * U+0080 and above (非ASCII Unicode 字符; 非URL安全字符,不推荐)
455 |
456 | 此外,以下字符也在键名称中也是合法的,只是不能出现在首位或末位:
457 |
458 | * U+002D HYPHEN-MINUS, “-“
459 | * U+005F LOW LINE, “_”
460 | * U+0020 SPACE, “ “ (非URL安全字符,不推荐)
461 |
462 | ####保留字符
463 |
464 | 以下字符不能被用于键名称中:
465 |
466 | * U+002B PLUS SIGN, “+” (用于排序)
467 | * U+002C COMMA, “,” (用于关联路径的分隔符)
468 | * U+002E PERIOD, “.” (用于关联路径中的分隔符)
469 | * U+005B LEFT SQUARE BRACKET, “[” (用于稀疏字段集合)
470 | * U+005D RIGHT SQUARE BRACKET, “]” (用于稀疏字段集合)
471 | * U+0021 EXCLAMATION MARK, “!”
472 | * U+0022 QUOTATION MARK, ‘”’
473 | * U+0023 NUMBER SIGN, “#”
474 | * U+0024 DOLLAR SIGN, “$”
475 | * U+0025 PERCENT SIGN, “%”
476 | * U+0026 AMPERSAND, “&”
477 | * U+0027 APOSTROPHE, “’”
478 | * U+0028 LEFT PARENTHESIS, “(“
479 | * U+0029 RIGHT PARENTHESIS, “)”
480 | * U+002A ASTERISK, “*”
481 | * U+002F SOLIDUS, “/”
482 | * U+003A COLON, “:”
483 | * U+003B SEMICOLON, “;”
484 | * U+003C LESS-THAN SIGN, “<”
485 | * U+003D EQUALS SIGN, “=”
486 | * U+003E GREATER-THAN SIGN, “>”
487 | * U+003F QUESTION MARK, “?”
488 | * U+0040 COMMERCIAL AT, “@”
489 | * U+005C REVERSE SOLIDUS, “"
490 | * U+005E CIRCUMFLEX ACCENT, “^”
491 | * U+0060 GRAVE ACCENT, “`”
492 | * U+007B LEFT CURLY BRACKET, “{“
493 | * U+007C VERTICAL LINE, “”
494 | * U+007D RIGHT CURLY BRACKET, “}”
495 | * U+007E TILDE, “~”
496 | * U+007F DELETE
497 | * U+0000 to U+001F (C0 Controls)
498 |
499 | ## 数据获取
500 |
501 | 数据包括资源和关联,可以通过向后端发送`GET`请求获取数据。
502 |
503 | 响应可以通过以下描述的可选特征进一步提取。
504 |
505 | ### 资源获取
506 |
507 | 服务器必须支持通过URL获取资源,URL可以通过以下形式提供:
508 |
509 | * top-level链接对象中的`self`链接.
510 | * resource-level链接对象中的`self`链接.
511 | * relationship-level链接对象中的`related`链接.
512 |
513 | 例如,以下请求获取一个文献集合:
514 |
515 | ```javascript
516 | GET /articles HTTP/1.1
517 | Accept: application/vnd.api+json
518 | ```
519 |
520 | 以下请求获取一篇文献:
521 |
522 | ```javascript
523 | GET /articles/1 HTTP/1.1
524 | Accept: application/vnd.api+json
525 | ```
526 |
527 | 以下请求获取一篇文献的作者:
528 |
529 | ```javascript
530 | GET /articles/1/author HTTP/1.1
531 | Accept: application/vnd.api+json
532 | ```
533 |
534 | #### 响应
535 |
536 | ##### 200 OK 状态码
537 |
538 | 如果服务器通过请求成功获取一个单独资源或资源集合,服务器必须以`200 OK`状态码回复。
539 |
540 | 如果服务器通过请求成功获取一个资源集合,服务器必须将一个[资源对象](http://jsonapi.org/format/#document-resource-objects)的列表或空列表(`[]`)作为响应文档的主数据。
541 |
542 | 例如,对于一个针对文献集合的`GET`请求可以回复:
543 |
544 | ```javascript
545 | HTTP/1.1 200 OK
546 | Content-Type: application/vnd.api+json
547 |
548 | {
549 | "links": {
550 | "self": "http://example.com/articles"
551 | },
552 | "data": [{
553 | "type": "articles",
554 | "id": "1",
555 | "attributes": {
556 | "title": "JSON API paints my bikeshed!"
557 | }
558 | }, {
559 | "type": "articles",
560 | "id": "2",
561 | "attributes": {
562 | "title": "Rails is Omakase"
563 | }
564 | }]
565 | }
566 | ```
567 |
568 | 一个空集合的相似回复可以是以下形式:
569 |
570 | ```javascript
571 | HTTP/1.1 200 OK
572 | Content-Type: application/vnd.api+json
573 |
574 | {
575 | "links": {
576 | "self": "http://example.com/articles"
577 | },
578 | "data": []
579 | }
580 | ```
581 |
582 | 如果服务器可以通过请求成功获取一个单独资源,服服务器必须将一个[资源对象](http://jsonapi.org/format/#document-resource-objects)或`null`作为响应文档的主数据。
583 |
584 | 只有当请求URL可能指向、但当前并不指向一个单独资源时,`null`才应当作为响应。
585 |
586 | 例如,一个针对一篇文献的请求可以通过以下形式回复:
587 |
588 | ```javascript
589 | HTTP/1.1 200 OK
590 | Content-Type: application/vnd.api+json
591 |
592 | {
593 | "links": {
594 | "self": "http://example.com/articles/1"
595 | },
596 | "data": {
597 | "type": "articles",
598 | "id": "1",
599 | "attributes": {
600 | "title": "JSON API paints my bikeshed!"
601 | },
602 | "relationships": {
603 | "author": {
604 | "links": {
605 | "related": "http://example.com/articles/1/author"
606 | }
607 | }
608 | }
609 | }
610 | }
611 | ```
612 |
613 | 如果以上文献的作者是缺失的,相关资源的`GET`请求可以通过以下形式回复:
614 |
615 | ```javasript
616 | HTTP/1.1 200 OK
617 | Content-Type: application/vnd.api+json
618 |
619 | {
620 | "links": {
621 | "self": "http://example.com/articles/1/author"
622 | },
623 | "data": null
624 | }
625 | ```
626 |
627 | ##### 404 Not Found 状态码
628 |
629 | 当通过一个请求获取一个不存在的单独资源时,服务器必须以`404 Not Found`状态码回复,除非请求允许`null`作为`200 OK`状态码响应的主数据(见后文描述)。
630 |
631 | ##### 其他响应
632 |
633 | 服务器可以回复其他HTTP状态码。
634 |
635 | 服务器可以在错误响应中包含[错误详情](http://jsonapi.org/format/#errors)。
636 |
637 | 服务器和客户端必须依据[HTTP语义](https://tools.ietf.org/html/rfc7231)分别生成和解析响应。
638 |
639 | ### 关联获取
640 |
641 | 服务器必须支持通过关联URL获取关联数据,关联URL可以通过关联的`links`对象中的`self`链接提供。
642 |
643 | 例如,以下请求获取文献的评论数据:
644 |
645 | ```javascript
646 | GET /articles/1/relationships/comments HTTP/1.1
647 | Accept: application/vnd.api+json
648 | ```
649 |
650 | 以下请求获取文献的作者数据:
651 |
652 | ```javascript
653 | GET /articles/1/relationships/author HTTP/1.1
654 | Accept: application/vnd.api+json
655 | ```
656 |
657 | #### 响应
658 |
659 | ##### 200 OK 状态码
660 |
661 | 当服务器通过请求成功获取关联时,服务器应该以`200 OK`状态码回复。
662 |
663 | 响应文档的主数据必须是与[资源连接](http://jsonapi.org/format/#document-resource-object-linkage)相匹配的值,如上文对[关联对象](http://jsonapi.org/format/#document-resource-object-relationships)的描述。
664 |
665 | Top-level[链接对象](http://jsonapi.org/format/#document-links)可以包含`self`和`related`链接,如上文对[关联对象](http://jsonapi.org/format/#document-resource-object-relationships)的描述。
666 |
667 | 例如,一个来自单对象关联链接的`GET`请求可以通过以下形式回复:
668 |
669 | ```javascript
670 | HTTP/1.1 200 OK
671 | Content-Type: application/vnd.api+json
672 |
673 | {
674 | "links": {
675 | "self": "/articles/1/relationships/author",
676 | "related": "/articles/1/author"
677 | },
678 | "data": {
679 | "type": "people",
680 | "id": "12"
681 | }
682 | }
683 | ```
684 |
685 | 如果以上关联为空,相同URL的`GET`请求可以通过以下形式回复:
686 |
687 | ```javascript
688 | HTTP/1.1 200 OK
689 | Content-Type: application/vnd.api+json
690 |
691 | {
692 | "links": {
693 | "self": "/articles/1/relationships/author",
694 | "related": "/articles/1/author"
695 | },
696 | "data": null
697 | }
698 | ```
699 |
700 | 一个来自多对象关联链接的`GET`请求可以通过以下形式回复:
701 |
702 | ```javascript
703 | HTTP/1.1 200 OK
704 | Content-Type: application/vnd.api+json
705 |
706 | {
707 | "links": {
708 | "self": "/articles/1/relationships/tags",
709 | "related": "/articles/1/tags"
710 | },
711 | "data": [
712 | { "type": "tags", "id": "2" },
713 | { "type": "tags", "id": "3" }
714 | ]
715 | }
716 | ```
717 |
718 | 如果以上关联为空,相同URL的`GET`请求可以通过以下形式回复:
719 |
720 | ```javascript
721 | HTTP/1.1 200 OK
722 | Content-Type: application/vnd.api+json
723 |
724 | {
725 | "links": {
726 | "self": "/articles/1/relationships/tags",
727 | "related": "/articles/1/tags"
728 | },
729 | "data": []
730 | }
731 | ```
732 |
733 | ##### 404 Not Found 状态码
734 |
735 | 当通过一个请求获取一个不存在的关联链接URL时,服务器必须以`404 Not Found`状态码回复。
736 |
737 | 如果关联链接URL存在但是关联为空,如前文所述,服务器必须以`200 OK`状态码回复。
738 |
739 | ##### 其他响应
740 |
741 | 服务器可以回复其他HTTP状态码。
742 |
743 | 服务器可以在错误响应中包含[错误详情](http://jsonapi.org/format/#errors)。
744 |
745 | 服务器和客户端必须依据[HTTP语义](https://tools.ietf.org/html/rfc7231)分别生成和解析响应。
746 |
747 | ### 内联资源
748 |
749 | 后端可以默认回复与主数据相关的资源。
750 |
751 | 后端也可以支持`include`请求参数以保证客户端可以指定需要回复的相关资源。
752 |
753 | 如果后端不支持`include`参数,必须以`400 Bad Request`状态码回复任何包含此参数的请求。
754 |
755 | 如果后端支持`include`参数并且客户端使用了此参数,服务器不能在[复合文档](http://jsonapi.org/format/#document-compound-documents)的`included`部分包含任何未请求的[资源对象](http://jsonapi.org/format/#document-resource-objects)。
756 |
757 | `include`参数的值必须是一个由逗号分隔符(U+002C COMMA, “,”) 分割的关联路径列表。关联路径是由点号分隔符(U+002E FULL-STOP, “.”) 分割的[关联](http://jsonapi.org/format/#document-resource-object-relationships)名称。
758 |
759 | 如果服务器不能识别关联路径或不能通过路径支持内联资源,必须以`400 Bad Request`状态码回复。
760 |
761 | 例如,可以同时请求评论和文献:
762 |
763 | ```javascript
764 | GET /articles/1?include=comments HTTP/1.1
765 | Accept: application/vnd.api+json
766 | ```
767 |
768 | 可以通过如下形式为每个关联名称定义逗号分割路径,以请求与其他资源相关的资源:
769 |
770 | ```javascript
771 | GET /articles/1?include=comments.author HTTP/1.1
772 | Accept: application/vnd.api+json
773 | ```
774 |
775 | 可以通过逗号分割列表请求多个相关资源:
776 |
777 | ```javascript
778 | GET /articles/1?include=author,comments.author HTTP/1.1
779 | Accept: application/vnd.api+json
780 | ```
781 |
782 | 此外,可以向关联后端请求相关资源:
783 |
784 | ```javascript
785 | GET /articles/1/relationships/comments?include=comments.author HTTP/1.1
786 | Accept: application/vnd.api+json
787 | ```
788 |
789 | 在这种情况下,主数据应该是代表此文献的评论连接的[资源标识符对象](http://jsonapi.org/format/#document-resource-identifier-objects)集合,所有评论和评论作者将会作为相关数据返回。
790 |
791 | ### 稀疏字段组
792 |
793 | 客户端可能通过`fields[TYPE]`参数,请求后端返回只包含特定字段的响应。
794 |
795 | `fields`参数的值必须用逗号分隔开,用来表示需要返回字段的名称。
796 |
797 | 如果客户端请求了一组给定类型的字段,那么后端不能包括资源对象内此类型的附加字段。
798 |
799 | ```javascript
800 | GET /articles?include=author&fields[articles]=title,body&fields[people]=name HTTP/1.1
801 | Accept: application/vnd.api+json
802 | ```
803 | ### 排序
804 |
805 | 服务器可以选择性支持,根据一个或多个条件(排序字段)对资源集合排序。
806 |
807 | 后端可能支持带有`sort`查询参数的请求,来排序主要数据。`sort`的值必须代表排序字段。
808 |
809 | ```javascript
810 | GET /people?sort=age HTTP/1.1
811 | Accept: application/vnd.api+json
812 | ```
813 |
814 | 后端可能支持带有多个排序字段的请求,允许用逗号分隔排序字段。排序字段应该被按照特定顺序执行。
815 |
816 | ```javascript
817 | GET /people?sort=age,name HTTP/1.1
818 | Accept: application/vnd.api+json
819 | ```
820 |
821 | 每个排序字段必须按升序排列。除非它带有`-`前缀,这种情况下将按降序排列。
822 |
823 | ```javascript
824 | GET /articles?sort=-created,title HTTP/1.1
825 | Accept: application/vnd.api+json
826 | ```
827 |
828 | 以上的例子应首先返回最新的条目。在同一天创建的条目讲按照标题的字母升序排列。
829 |
830 | 如果服务器不支持查询参数`sort`指定的排序,必须返回`400 Bad Request`。
831 |
832 | 如果服务器支持排序,并且客户端通过查询参数`sort`进行排序,服务器必须返回根据指定条件排序的,
833 | 上层`data`数组的元素。
834 |
835 | ### 分页
836 |
837 | 服务器可以选择性限制响应返回的资源数量,为所有可获取资源的子集("page")。
838 |
839 | 服务器可以提供传送分页后数据集的连接("pagination links")。
840 |
841 | `pagination links`必须出现在集合相关的连接对象中。需要在上层`links`对象中提供`pagination links`。
842 |
843 | 下面的键必须被用于`pagination links`:
844 | * `first`: 第一页数据
845 | * `last`: 最后一页数据
846 | * `prev`: 前一页数据
847 | * `next`: 后一页数据
848 |
849 | 如需表示特定的连接不可用,这些键必须被省略,或者值为`null`。
850 |
851 | 分页的排序,必须与JSON API的排序规则一致。
852 |
853 | 查询参数`page`是分页的保留字,服务器和客户端应该用这个参数进行分页。
854 |
855 |
856 | ### 过滤
857 |
858 | 查询参数`filter`是过滤的保留字,服务器和客户端应该用这个参数进行过滤。
859 |
860 |
861 | ## 创建,更新,删除资源
862 |
863 | 服务器可能支持给定类型资源的创建,服务器也可能允许已存在资源的更新和删除。
864 |
865 | 服务器允许单次请求,更新多个资源,如下所述。多个资源更新必须完全成功或者失败,不允许部分更新成功。
866 |
867 | 任何包含内容的请求,必须包含`Content-Type:application/vnd.api+json`请求头。
868 |
869 | ### 创建资源
870 |
871 |
872 | 向表示待创建资源所属资源集的URL,发出`POST`请求,创建一个或多个资源。
873 | 请求必须包含单一资源对象作为主数据。资源对象必须包含至少一个`type`成员。
874 |
875 | 例如,新photo可以通过如下请求创建:
876 |
877 | ```javascript
878 | POST /photos HTTP/1.1
879 | Content-Type: application/vnd.api+json
880 | Accept: application/vnd.api+json
881 |
882 | {
883 | "data": {
884 | "type": "photos",
885 | "attributes": {
886 | "title": "Ember Hamster",
887 | "src": "http://example.com/images/productivity.png"
888 | },
889 | "relationships": {
890 | "photographer": {
891 | "data": { "type": "people", "id": "9" }
892 | }
893 | }
894 | }
895 | }
896 | ```
897 | 如果资源对象在`relationships`提供了关系,它的值必须是一个有`data`成员的关系对象。
898 | 这个键的值代表新资源将要有的连接。
899 |
900 | #### 客户端生成的ID
901 |
902 | 服务器可能接受创建资源的请求中有客户端生成的ID。ID必须被`id`键指定,它的值必须是通用唯一识别码。
903 | 客户端应该使用 RGC4122 [`RGC4122`](http://tools.ietf.org/html/draft-ietf-
904 | httpbis-p2-semantics-22#section-6.3)中描述的合适的UUID。
905 |
906 | 比如:
907 |
908 | ```javascript
909 | POST /photos HTTP/1.1
910 | Content-Type: application/vnd.api+json
911 | Accept: application/vnd.api+json
912 |
913 | {
914 | "data": {
915 | "type": "photos",
916 | "id": "550e8400-e29b-41d4-a716-446655440000",
917 | "attributes": {
918 | "title": "Ember Hamster",
919 | "src": "http://example.com/images/productivity.png"
920 | }
921 | }
922 | }
923 | ```
924 |
925 | 服务器必须对不支持的带有客户端生成ID的创建请求返回`403 Forbidden`。
926 |
927 |
928 | #### 响应
929 |
930 | ##### 201 Created
931 |
932 | 如果`POST`请求不包括客户端生成的ID,并且请求的资源成功被创建,服务器必须返回`201 Created`状态码。
933 |
934 | 响应应该包含`Location`头,用以标示请求创建所有资源的位置。
935 |
936 | 响应必须含有一个文档,用以存储所创建的主要资源。
937 |
938 | 如果响应返回的资源对象在`links`成员里包含`self`键,并且响应数据头提供了`Location`,
939 | 那么`self`的值必须匹配`Location`的值。
940 |
941 | ```javascript
942 | HTTP/1.1 201 Created
943 | Location: http://example.com/photos/550e8400-e29b-41d4-a716-446655440000
944 | Content-Type: application/vnd.api+json
945 |
946 | {
947 | "data": {
948 | "type": "photos",
949 | "id": "550e8400-e29b-41d4-a716-446655440000",
950 | "attributes": {
951 | "title": "Ember Hamster",
952 | "src": "http://example.com/images/productivity.png"
953 | },
954 | "links": {
955 | "self": "http://example.com/photos/550e8400-e29b-41d4-a716-446655440000"
956 | }
957 | }
958 | }
959 | ```
960 |
961 | ##### 202 Accepted
962 |
963 | 如果创建资源的请求被接受处理,但在服务器响应时处理并未完成,那么服务器必须返回`202 Accepted`状态码。
964 |
965 | ##### 204 No Content
966 |
967 | 如果`POST`请求包括客户端生成的ID,并且请求的资源成功被创建,那么服务器必须返回`201 Created`状态码和响应文档(如上所述),
968 | ,或者只返回`204 No Content`状态码,没有响应文档。
969 |
970 | ##### 403 Forbidden
971 |
972 | 服务器可能向不支持的创建资源的请求返回`403 Forbidden`的响应。
973 |
974 | ##### 404 Not Found
975 |
976 | 如果创建请求引用的相关资源不存在,服务器必须返回`404 Not Found`的响应。
977 |
978 | ##### 409 Conflict
979 |
980 | 如果创建请求中,客户端生成的ID已经存在,服务器必须返回`409 Conflict`的响应。
981 |
982 | 如果创建请求中,资源对象的`type`不在后端支持的类型里,服务器必须返回`409 Conflict`的响应。
983 |
984 | 服务器应该在响应中包括错误详情和足够的信息以识别冲突原因。
985 |
986 | ##### Other Responses
987 |
988 | 服务器响应可能没有状态码。
989 |
990 | 服务器可能响应包括错误详情的错误响应。
991 |
992 | 服务器与客户端必须依照HTTP的语义准备和解译响应。
993 |
994 |
995 | ### 更新资源
996 |
997 | 向表示资源的URL发出`PATCH`请求,即可进行资源更新。
998 |
999 | 资源的URL可以从资源对象的`self`的值获得。或者,当`GET`请求返回了一个资源对象作为主资源,
1000 | 同样的请求URL可被用来更新资源。
1001 |
1002 | `PATCH`请求必须包括一个资源对象作为主资源。资源对象必须包含`type`和`id`成员。
1003 |
1004 | 比如:
1005 |
1006 | ```javascript
1007 | PATCH /articles/1 HTTP/1.1
1008 | Content-Type: application/vnd.api+json
1009 | Accept: application/vnd.api+json
1010 |
1011 | {
1012 | "data": {
1013 | "type": "articles",
1014 | "id": "1",
1015 | "attributes": {
1016 | "title": "To TDD or Not"
1017 | }
1018 | }
1019 | }
1020 | ```
1021 |
1022 | #### 更新资源属性
1023 |
1024 | 资源的任何一个,或者所有属性,可能被包括在`PATCH`请求的资源对象中。
1025 |
1026 | 如果请求不包括资源所有的属性,那么服务器解译请求时必须添加这些属性,并赋予当前的值。
1027 | 服务器不能给缺失的属性赋值为null。
1028 |
1029 | 比如,下面的`PATCH`请求被解译为只更新`title`和`text`两个属性:
1030 |
1031 | ```javascript
1032 | PATCH /articles/1 HTTP/1.1
1033 | Content-Type: application/vnd.api+json
1034 | Accept: application/vnd.api+json
1035 |
1036 | {
1037 | "data": {
1038 | "type": "articles",
1039 | "id": "1",
1040 | "attributes": {
1041 | "title": "To TDD or Not",
1042 | "text": "TLDR; It's complicated... but check your test coverage regardless."
1043 | }
1044 | }
1045 | }
1046 | ```
1047 |
1048 | #### 更新资源关联
1049 |
1050 | 资源的所有或者任何一个关联都可能包括在`PATCH`请求的资源对象中。
1051 |
1052 | 如果一个请求不包括资源所有的关联,那么服务器解译请求时必须添加这些属性,并赋予当前的值。
1053 | 服务器不能给缺失的属性赋值为null或者为空。
1054 |
1055 | 如果关联是被`PATCH`请求的资源对象中`relationships`给出的,那么它的值必须是一个有`data`成员的关联对象。
1056 | 关联的值将被这个成员定义的值代替。
1057 |
1058 | 比如,下面的`PATCH`请求将会更新`author`关联:
1059 |
1060 | ```javascript
1061 | PATCH /articles/1 HTTP/1.1
1062 | Content-Type: application/vnd.api+json
1063 | Accept: application/vnd.api+json
1064 |
1065 | {
1066 | "data": {
1067 | "type": "articles",
1068 | "id": "1",
1069 | "relationships": {
1070 | "author": {
1071 | "data": { "type": "people", "id": "1" }
1072 | }
1073 | }
1074 | }
1075 | }
1076 | ```
1077 |
1078 | 相似的,下面的`PATCH`请求会更新整个`tags`关联:
1079 |
1080 | ```javascript
1081 | PATCH /articles/1 HTTP/1.1
1082 | Content-Type: application/vnd.api+json
1083 | Accept: application/vnd.api+json
1084 |
1085 | {
1086 | "data": {
1087 | "type": "articles",
1088 | "id": "1",
1089 | "relationships": {
1090 | "tags": {
1091 | "data": [
1092 | { "type": "tags", "id": "2" },
1093 | { "type": "tags", "id": "3" }
1094 | ]
1095 | }
1096 | }
1097 | }
1098 | }
1099 | ```
1100 |
1101 | 服务器可能拒绝更新多对象关联的请求。在这种情况,服务器必须阻止整个更新,并返回`403 Forbidden`状态码。
1102 |
1103 | ### 响应
1104 |
1105 | #### 202 Accepted
1106 |
1107 | 如果服务器接受更新,但在服务器响应时更新并未完成,那么服务器必须返回`202 Accepted`状态码。
1108 |
1109 | #### 204 No Content
1110 |
1111 | 如果更新成功,且服务器没有更新任何未提供的属性,那么服务器必须或者返回`204 No Content`状态码和响应文档,或者只返回`204 No Content`状态码,没有响应文档。
1112 |
1113 | #### 200 OK
1114 |
1115 | 如果服务器接受更新,但是在请求指定内容之外做了资源修改,必须响应`200 OK`以及更新的资源实例,像是向此URL发出`GET`请求。
1116 |
1117 | 如果更新成功,客户端当前属性保持更新,并且服务器只响应最上层元数据,那么服务器必须返回`200 OK`状态码。
1118 | 这种情况下,服务器不能包括更新后的资源。
1119 |
1120 | #### 其它响应
1121 |
1122 | 服务器使用其它HTTP错误状态码反映错误。
1123 | 客户端必须依据HTTP规范处理这些错误信息。
1124 | 如下所述,错误细节可能会一并返回。
1125 |
1126 | ### 更新关联
1127 |
1128 | 虽然关联可以通过资源被更新(如上所述),JSON API也支持单独通过`Relationship Links`更新关联。
1129 |
1130 | #### 更新To-One 关联
1131 |
1132 | `PATCH`请求必须包括上层名为`data`的成员,包括:
1133 |
1134 | 相关新资源的资源表示对象
1135 |
1136 | 或者
1137 |
1138 | `null`,用来删除关联。
1139 |
1140 | 比如,下面的请求更新author关联:
1141 |
1142 | ```javascript
1143 | PATCH /articles/1/relationships/author HTTP/1.1
1144 | Content-Type: application/vnd.api+json
1145 | Accept: application/vnd.api+json
1146 |
1147 | {
1148 | "data": { "type": "people", "id": "12" }
1149 | }
1150 | ```
1151 |
1152 | 而下面的请求会清除author关联:
1153 |
1154 | ```javascript
1155 | PATCH /articles/1/relationships/author HTTP/1.1
1156 | Content-Type: application/vnd.api+json
1157 | Accept: application/vnd.api+json
1158 |
1159 | {
1160 | "data": null
1161 | }
1162 | ```
1163 |
1164 | 如果关联被成功更新,服务器必须返回一个成功的响应。
1165 |
1166 | #### 更新To-Many 关联
1167 |
1168 | 对所有请求类型,主体必须包括一个`data`成员,其值是一个空数组,或是一个`资源标识对象`的数组。
1169 |
1170 | 如果客户端向一个to-many关联连接的URL发出`PATCH`请求,服务器必须或者完全更改关联的每一个成员,
1171 | 如果资源不存在或者无法使用,返回合适的错误状态码,如果服务器不允许完全更改,则返回`403 Forbidden`。
1172 |
1173 | 比如,下面的请求会更改所有的`tag`:
1174 |
1175 | ```javascript
1176 | PATCH /articles/1/relationships/tags HTTP/1.1
1177 | Content-Type: application/vnd.api+json
1178 | Accept: application/vnd.api+json
1179 |
1180 | {
1181 | "data": [
1182 | { "type": "tags", "id": "2" },
1183 | { "type": "tags", "id": "3" }
1184 | ]
1185 | }
1186 | ```
1187 |
1188 | 而下面的请求会清除所有的`tag`:
1189 |
1190 | ```javascript
1191 | PATCH /articles/1/relationships/tags HTTP/1.1
1192 | Content-Type: application/vnd.api+json
1193 | Accept: application/vnd.api+json
1194 |
1195 | {
1196 | "data": []
1197 | }
1198 | ```
1199 |
1200 | 如果客户端向一个关联连接的URL发出`POST`请求,那么服务器必须向关联添加新增的成员,
1201 | 除非他们已经存在。如果所给的`type`和`id`已经存在,服务器不能再添加他们。
1202 |
1203 | 如果所有指定的资源都可以被添加到关联,或者已经存在在关联里,那么服务器必须返回一个成功响应。
1204 |
1205 | 在下面的例子中,ID为`123`的评论被添加到评论列表内ID为`1`的条目中。
1206 |
1207 | ```javascript
1208 | POST /articles/1/relationships/comments HTTP/1.1
1209 | Content-Type: application/vnd.api+json
1210 | Accept: application/vnd.api+json
1211 |
1212 | {
1213 | "data": [
1214 | { "type": "comments", "id": "123" }
1215 | ]
1216 | }
1217 | ```
1218 |
1219 | 如果客户端向一个关联连接的URL发出`DELETE`请求,那么服务器必须在关联中删除指定的成员,或者返回`403 Forbidden`响应。
1220 | 如果所有指定的资源都可以从关联中删除,或者未存在在关联中,那么服务器必须返回一个成功响应。
1221 |
1222 | 关联成员是被与`POST`请求同样的方式指定的。
1223 |
1224 | 在下面的例子中,ID为`12`和`13`的评论被从评论列表内ID为`1`的条目中删除:
1225 |
1226 | ```javascript
1227 | DELETE /articles/1/relationships/comments HTTP/1.1
1228 | Content-Type: application/vnd.api+json
1229 | Accept: application/vnd.api+json
1230 |
1231 | {
1232 | "data": [
1233 | { "type": "comments", "id": "12" },
1234 | { "type": "comments", "id": "13" }
1235 | ]
1236 | }
1237 | ```
1238 | ### 响应
1239 |
1240 | #### 202 Accepted
1241 |
1242 | 如果服务器接受关联更新请求,但在服务器响应时更新并未完成,那么服务器必须返回`202 Accepted`状态码。
1243 |
1244 | #### 204 No Content
1245 |
1246 | 如果更新成功,并且请求中的资源与结果相符,那么服务器必须或者返回`204 No Content`状态码。
1247 |
1248 | #### 200 OK
1249 |
1250 | 如果服务器接受更新,但用在请求指定以外的方式更改了目标关联,那么b必须返回`200 OK`响应。
1251 | 响应文档必须包括被更新关联的代表。
1252 |
1253 | 如果更新成功,客户端当前数据保持更新,并且服务器只响应最上层元数据,那么服务器必须返回`200 OK`状态码。
1254 | 这种情况下,服务器不能包括更新后的资源。
1255 |
1256 | #### 403 OK
1257 |
1258 | 服务器必须向不支持的更新关联请求发出`403 Forbidden`响应。
1259 |
1260 | #### 其它响应
1261 |
1262 | 服务器可能使用其它HTTP错误状态码反映错误。
1263 |
1264 | 服务器可能响应包括错误详情的错误响应。
1265 |
1266 | 服务器与客户端必须依照HTTP的语义准备和解译响应。
1267 |
1268 |
1269 | ### 资源删除
1270 |
1271 | 向资源URL发出`DELETE`请求即可删除单个资源。
1272 |
1273 | ```text
1274 | DELETE /photos/1 HTTP/1.1
1275 | Accept: application/vnd.api+json
1276 | ```
1277 |
1278 | #### 响应
1279 |
1280 | ##### 202 Accepted
1281 |
1282 | 如果删除资源的请求被接受处理,但在服务器响应时处理并未完成,那么服务器必须返回`202 Accepted`状态码。
1283 |
1284 | ##### 204 No Content
1285 |
1286 | 如果删除请求成功,服务器必须返回`204 No Content` 状态码,并且没有返回内容
1287 |
1288 | ##### 200 OK
1289 |
1290 | 如果删除请求成功,服务器必须返回`200 OK`状态码,并且返回上层的元数据。
1291 |
1292 | ##### 404 NOT FOUND
1293 |
1294 | 如果因为资源不存在,删除请求失败,那么服务器应该返回`404 Not Found`状态码。
1295 |
1296 | ##### 其它响应
1297 |
1298 | 服务器可能使用其它HTTP错误状态码反映错误。
1299 |
1300 | 服务器可能响应包括错误详情的错误响应。
1301 |
1302 | 服务器与客户端必须依照HTTP的语义准备和解译响应。
1303 |
1304 |
1305 | ## 查询参数
1306 |
1307 | 查询参数必须遵守成员命名的规则,同时必须包含至少一个非a-z字符(U+0061 to U+007A)。推荐使用 U+002D HYPHEN-MINUS, “-“, U+005F LOW LINE, “_”, 或者大写字母(如: camelCasing)。
1308 |
1309 | 如果服务器获取到不遵从以上命名规则的查询参数,并且不知道如何处理,那么服务器必须返回`400 Bad Request`。
1310 |
1311 |
1312 | ## 错误
1313 |
1314 | #### 处理错误
1315 |
1316 | 当遇到一个问题时,服务器可以选择停止处理,也可以继续处理,遇到多个问题。
1317 | 比如,服务器可能处理多个属性,并在一个响应中返回多个验证问题。
1318 |
1319 | 当服务器在一个请求中遇到多个问题,在响应中应该使用HTTP最通用的错误状态码。
1320 | 比如,`400 Bad Request`可被用于多个4xx错误,`500 Internal Server Error`可被用于多个5xx错误。
1321 |
1322 | #### 错误对象
1323 |
1324 | 错误对象提供执行操作时遇到问题的额外信息。
1325 | 在JSON API文档顶层,错误对象必须被作为`errors`键对应的数组返回。
1326 |
1327 | 错误对象可能有以下成员:
1328 |
1329 | * `id` - 特定问题的唯一标识符。
1330 | * `links` - 一个包括以下成员的连接对象:
1331 | * `about` - 指向特定问题更多具体内容的连接。
1332 | * `status` - 适用于这个问题的HTTP状态码,使用字符串表示。
1333 | * `code` - 应用特定的错误码,以字符串表示。
1334 | * `title` - 简短的,可读性高的问题总结。除了国际化本地化处理之外,不同场景下,相同的问题,值是不应该变动的。
1335 | * `detail` - 针对该问题的高可读性解释。与`title`相同,这个值可以被本地化。
1336 | * `source` - 包括错误资源的引用的对象。它也可以包括以下任意成员:
1337 | * `pointer` - 一个指向请求文档中相关实体的`JSON Pointer`。
1338 | * `parameter` - 指出引起错误的查询对象的字符串。
1339 | * `meta` - 一个包括关于错误的非标准元信息的元对象。
1340 |
--------------------------------------------------------------------------------
/images/blockquote.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justjavac/json-api-zh_CN/f33dd60e301338a59829ee2a2bfdabdff7ddc6e7/images/blockquote.gif
--------------------------------------------------------------------------------
/images/forkme_right_red_aa0000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justjavac/json-api-zh_CN/f33dd60e301338a59829ee2a2bfdabdff7ddc6e7/images/forkme_right_red_aa0000.png
--------------------------------------------------------------------------------
/images/jsonapi@0.7x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justjavac/json-api-zh_CN/f33dd60e301338a59829ee2a2bfdabdff7ddc6e7/images/jsonapi@0.7x.png
--------------------------------------------------------------------------------
/images/jsonapi@1.3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justjavac/json-api-zh_CN/f33dd60e301338a59829ee2a2bfdabdff7ddc6e7/images/jsonapi@1.3x.png
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: "用 JSON 构建 API 的标准指南"
4 | show_masthead: true
5 | ---
6 |
7 | 如果你和你的团队曾经争论过使用什么方式构建合理 JSON 响应格式,
8 | 那么 JSON API 就是你的 anti-bikeshedding 武器。
9 |
10 | 通过遵循共同的约定,可以提高开发效率,利用更普遍的工具,可以是你更加专注于开发重点:你的程序。
11 |
12 | 基于 JSON API 的客户端还能够充分利用缓存,以提升性能,有时甚至可以完全不需要网络请求。
13 |
14 | 下面是一个使用 JSON API 发送响应(response)的示例:
15 |
16 | ```javascript
17 | {
18 | "links": {
19 | "posts.author": {
20 | "href": "http://example.com/people/{posts.author}",
21 | "type": "people"
22 | },
23 | "posts.comments": {
24 | "href": "http://example.com/comments/{posts.comments}",
25 | "type": "comments"
26 | }
27 | },
28 | "posts": [{
29 | "id": "1",
30 | "title": "Rails is Omakase",
31 | "links": {
32 | "author": "9",
33 | "comments": [ "5", "12", "17", "20" ]
34 | }
35 | }]
36 | }
37 | ```
38 |
39 | 顶级的 `"links"` 部分是可选的。
40 | 除去 `"links"` 部分,此响应看起来非常接近使用已经存在的 API 构建的响应。
41 |
42 | JSON API 不仅可以用来构建响应,还包括创建和更新资源。
43 |
44 | {% include status.md %}
45 |
46 | ## MIME 类型
47 |
48 | JSON API 已经在 IANA 机构完成注册。
49 | 它的 MIME 类型是 [`application/vnd.api+json`](http://www.iana.org/assignments/media-types/application/vnd.api+json)。
50 |
51 | ## 格式
52 |
53 | 在开始使用 JSON API 前,先查看一下[JSON API 格式文档](/format)
54 |
55 | ## 更新历史
56 |
57 | - 2013-05-03:最初版本的草案。
58 | - 2013-07-22:媒体类型在 IANA 注册完成。
59 |
60 | 你可以使用 RSS 阅读器在[这里](https://github.com/json-api/json-api/commits.atom)订阅本提要的变更。
61 |
--------------------------------------------------------------------------------
/javascripts/all.js:
--------------------------------------------------------------------------------
1 | //= require_tree .
--------------------------------------------------------------------------------
/javascripts/highlight.pack.js:
--------------------------------------------------------------------------------
1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.ruby=function(e){var a="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var j="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var g={keyword:"and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include"};var c={cN:"yardoctag",b:"@[A-Za-z]+"};var k=[{cN:"comment",b:"#",e:"$",c:[c]},{cN:"comment",b:"^\\=begin",e:"^\\=end",c:[c],r:10},{cN:"comment",b:"^__END__",e:"\\n$"}];var d={cN:"subst",b:"#\\{",e:"}",l:a,k:g};var i=[e.BE,d];var b=[{cN:"string",b:"'",e:"'",c:i,r:0},{cN:"string",b:'"',e:'"',c:i,r:0},{cN:"string",b:"%[qw]?\\(",e:"\\)",c:i},{cN:"string",b:"%[qw]?\\[",e:"\\]",c:i},{cN:"string",b:"%[qw]?{",e:"}",c:i},{cN:"string",b:"%[qw]?<",e:">",c:i,r:10},{cN:"string",b:"%[qw]?/",e:"/",c:i,r:10},{cN:"string",b:"%[qw]?%",e:"%",c:i,r:10},{cN:"string",b:"%[qw]?-",e:"-",c:i,r:10},{cN:"string",b:"%[qw]?\\|",e:"\\|",c:i,r:10}];var h={cN:"function",bWK:true,e:" |$|;",k:"def",c:[{cN:"title",b:j,l:a,k:g},{cN:"params",b:"\\(",e:"\\)",l:a,k:g}].concat(k)};var f=k.concat(b.concat([{cN:"class",bWK:true,e:"$|;",k:"class module",c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(k)},h,{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:b.concat([{b:j}]),r:0},{cN:"symbol",b:a+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:k.concat([{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[e.BE,d]}]),r:0}]));d.c=f;h.c[1].c=f;return{l:a,k:g,c:f}}(hljs);hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.css=function(a){var b={cN:"function",b:a.IR+"\\(",e:"\\)",c:[a.NM,a.ASM,a.QSM]};return{cI:true,i:"[=/|']",c:[a.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:"import page media charset",c:[b,a.ASM,a.QSM,a.NM]},{cN:"tag",b:a.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[a.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[b,a.NM,a.QSM,a.ASM,a.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"