互联网工程任务组 | H. Andrews,编辑 |
互联网草案 | Cloudflare,Inc. |
预期状态:信息性 | A. Wright,编辑 |
过期日期:2018 年 7 月 23 日 | 2018 年 1 月 19 日 |
JSON 超级模式:用于 JSON 超媒体注释的词汇表
draft-handrews-json-schema-hyperschema-01
JSON 模式是一种基于 JSON 的格式,用于使用各种词汇表描述 JSON 数据。本文档指定了一种词汇表,用于使用超链接注释 JSON 文档。这些超链接包括描述如何通过 HTTP 等超媒体环境操作和交互远程资源的属性,以及根据实例值确定链接是否可用。本文档中描述的超链接序列化格式也可独立于 JSON 模式使用。
此草案的问题列表可在 <https://github.com/json-schema-org/json-schema-spec/issues> 中找到。
有关更多信息,请参阅 <https://json-schema.fullstack.org.cn/>。
要提供反馈,请使用此问题跟踪器、主页上列出的通信方式,或向文档编辑发送电子邮件。
此互联网草案完全符合 BCP 78 和 BCP 79 的规定提交。
互联网草案是互联网工程任务组 (IETF) 的工作文档。请注意,其他组织也可能将工作文档作为互联网草案分发。当前互联网草案的列表位于 http://datatracker.ietf.org/drafts/current/。
互联网草案是有效期最长为六个月的草案文档,并可能随时被其他文档更新、替换或废弃。不应将互联网草案用作参考材料或引用它们,除非是作为“正在进行的工作”。
此互联网草案将于 2018 年 7 月 23 日过期。
版权所有 (c) 2018 IETF 信托基金会以及被认定为文档作者的人员。版权所有。
本文件受 BCP 78 和 IETF 信托基金会关于 IETF 文档的法律条款 (http://trustee.ietf.org/license-info) 约束,这些条款在该文件发布之日生效。请仔细阅读这些文件,因为它们描述了您对该文件的权利和限制。从该文档中提取的代码组件必须包含简化 BSD 许可证文本,如信托法律条款第 4.e 节所述,并且按简化 BSD 许可证中所述的方式提供,不提供任何担保。
JSON 超级模式是一个 JSON 模式词汇表,用于使用超链接和指令注释 JSON 文档,以便通过 HTTP 等超媒体环境处理和操作远程 JSON 资源。
术语 JSON 超级模式用于指代使用这些关键字的 JSON 模式。术语“超级模式”本身指的是此规范范围内的 JSON 超级模式。
用于指定链接的主要机制是链接描述对象 (LDO),它是 RFC 8288 第 2 节 [RFC8288] 中定义的抽象链接模型的序列化。
本规范将使用 JSON 模式核心 [json-schema] 和 JSON 模式验证 [json-schema-validation] 规范中定义的概念、语法和术语。建议读者拥有一份这些规范的副本。
本文档中的关键词“必须”、“禁止”、“必需”、“应”、“不应”、“建议”、“不建议”、“推荐”、“可以”和“可选”应按 RFC 2119 [RFC2119] 中的描述进行解释。
JSON 超级模式通过描述如何从实例数据构建超链接,使从 JSON 文档构建超媒体系统成为可能。
JSON 实例文档和该实例的有效 application/schema+json 超级模式的组合充当单个超媒体表示。通过允许这种分离,基于超级模式的系统可以优雅地支持期望纯 JSON 的应用程序,同时为支持超级模式的应用程序和用户代理提供完整的超媒体功能。
用户代理可以通过查找 application/schema+json 媒体类型和指示存在超级模式词汇表的 "$schema" 值来检测超级模式的存在。然后,用户代理可以使用 JSON 超级模式的实现将模式和实例文档的组合作为一个资源的单个逻辑表示进行处理,就像使用任何单个文档超媒体表示格式一样。
超级模式允许表示在网络上传输时占用更少的字节,并将链接构建的负担从服务器分发到每个客户端。用户代理无需构建链接,除非客户端应用程序请求该链接。JSON 超级模式还可以在服务器端用于在运行时生成其他链接序列化或表示格式,或预先跟随链接以促进服务器推送使用。
以下是一个示例超级模式,它添加了一个带有 IANA 注册的链接关系类型“self”的链接,该链接是从具有名为“id”的一个已知对象字段的实例构建的
{ "type": "object", "properties": { "id": { "type": "number", "readOnly": true } }, "links": [ { "rel": "self", "href": "thing/{id}" } ] }
如果实例是 {"id": 1234},并且根据 RFC 3986 第 5.1 节 [RFC3986],其基本 URI 为“https://api.example.com/”,那么“https://api.example.com/thing/1234”是结果链接的目标 URI。
术语“模式”、“实例”和“元模式”应按 JSON 模式核心规范 [json-schema] 中的定义进行解释。
术语“适用”和“附加”应按 JSON 模式验证规范第 3 节 [json-schema-validation] 中的定义进行解释。
术语“链接”、“链接上下文”(或“上下文”)、“链接目标”(或“目标”)和“目标属性”应按 RFC 8288 第 2 节 [RFC8288] 中的定义进行解释。
术语“用户代理”应按 RFC 7230 第 2.1 节 [RFC7230] 中的定义进行解释,将其推广为适用于任何协议,这些协议可能用于超媒体系统,而不是专门作为 HTTP 客户端。
本规范定义了以下术语
JSON 超级模式实现能够获取超级模式、实例,并在某些情况下获取客户端输入,并生成一组完全解析的有效链接。如 RFC 8288,第 2 节 [RFC8288] 所定义,链接由上下文、类型化关系、目标以及可选的附加目标属性组成。
关系类型和目标属性直接取自每个链接的链接描述对象。上下文和目标标识符由 URI 模板、实例数据以及(在目标标识符的情况下)客户端输入的某种组合构建。
目标始终由 URI 完全识别。由于缺乏用于 application/json 和许多其他可与 JSON 超级模式一起使用的媒体类型的 URI 片段标识符语法,上下文可能仅由 URI 部分识别。在这种情况下,剩余的标识将作为 JSON 指针提供。
JSON 超级模式文档中,几个 IANA 注册的链接关系类型被赋予了特定的语义。一个“self”链接用于与实例文档所表示的资源进行交互,而“collection”和“item”链接识别可以假定集合特定语义的资源。
JSON 超级模式元模式的当前 URI 是 <https://json-schema.fullstack.org.cn/draft-07/hyper-schema#>.
该 链接描述格式 [ldo] 可以不使用 JSON Schema 来使用,并且可以通过将规范性链接描述模式作为使用链接的数据结构的模式来引用来声明此格式的使用。规范性链接描述模式的 URI 是:<https://json-schema.fullstack.org.cn/draft-07/links#>.
JSON 超级模式实现可以自由地以任何格式提供输出。但是,为符合性测试套件定义了一种特定格式,该格式也用于说明 “实现要求” [implementation] 中的要点,以及显示由 示例 [examples] 生成的输出。建议实现能够以这种格式生成输出以方便测试。描述推荐输出格式的 JSON Schema 的 URI 是 <https://json-schema.fullstack.org.cn/draft-07/hyper-schema-output#>.
来自所有适用于实例中某个位置的模式的超级模式关键字,如 JSON Schema 验证的第 3 节 [json-schema-validation] 所定义,可以与该实例一起使用。
当多个子模式适用于给定的子实例时,所有“link”数组都必须以任何顺序组合到一个单独的集合中。结果集合中的每个对象都必须保留其自身适用的“base”值列表,按照解析顺序,来自同一模式和任何父模式。
与所有 JSON Schema 关键字一样,本节中描述的所有关键字都是可选的。最小的有效 JSON 超级模式是空对象。
如果存在,此关键字必须首先 解析为 URI 模板 [uriTemplating],然后必须解析为针对实例的当前 URI 基准的 URI 引用。结果必须在处理包含“base”的子模式及其中的所有子模式时,设置为实例的新 URI 基准。
当解析为与“anchor”一起使用时,解析“base”模板的过程可能与解析为与“href”一起使用时不同,这一点在 URI 模板部分有详细说明。
模式的“links”属性用于将链接描述对象与实例相关联。此属性的值必须是一个数组,数组中的项目必须是链接描述对象,如下所定义。
链接描述对象 (LDO) 是 RFC 8288,第 2 节 [RFC8288] 中定义的抽象链接模型的序列化。如该文档所述,链接由上下文、关系类型、目标以及可选的目标属性组成。JSON 超级模式的 LDO 提供了所有这些内容,以及使用 JSON Schema 来描述以各种方式与链接一起使用的输入的附加功能。
由于使用 URI 模板来标识链接上下文和目标,以及可选地在标识目标时进一步使用客户端输入,因此 LDO 是一个链接模板,它在与 JSON 实例文档一起使用时可能会解析为多个链接。
LDO 的特定用途,通常涉及跨协议的请求和响应,被称为操作。对于许多协议,可以在任何给定链接上执行多个操作。协议由目标的 URI 方案指示。请注意,并非所有 URI 方案都指示可用于通信的协议,即使具有指示此类协议的 URI 方案的资源也不一定可以通过该协议获得。
链接描述对象必须是一个对象,并且 “href” [href] 和 “rel” [rel] 属性必须存在。每个关键字在本节中都有简要介绍,并在本文档后面的部分提供更多用法说明和综合示例。
在 JSON 超级模式中,链接的上下文资源默认情况下是它所附加的子实例(如 JSON Schema 验证规范的第 3 节 [json-schema-validation] 所定义)。这通常不是整个实例文档。可以使用本节中的关键字更改此默认上下文。
根据实例的媒体类型,可能无法或可能无法为确切的默认上下文资源分配 URI。特别是,application/json 没有定义 URI 片段解析语法,因此普通 JSON 文档中的属性或数组元素无法由 URI 完全识别。当无法生成完整的 URI 时,上下文的位置应该由实例文档的 URI 以及一个单独的纯字符串 JSON 指针来传达。
实现必须能够构建链接上下文的 URI,以及(如果需要完整识别),一个根据 RFC 6901,第 5 节 [RFC6901] 以字符串表示形式存在的 JSON 指针,以代替 URI 片段。基于 URI 模板构建 URI 的过程在 URI 模板 [uriTemplating] 部分给出。
此属性设置链接的上下文 URI。属性的值是 URI 模板 [RFC6570],并且结果 URI 引用 [RFC3986] 必须解析为针对实例的基准 URI。
使用与 “href” [href] 属性相同的过程,从提供的 URI 模板计算 URI,但 “hrefSchema” [hrefSchema] 必须不存在。与目标 URI 不同,上下文 URI 不接受用户输入。
此属性更改实例中被视为链接上下文资源的点。属性的值必须是一个有效的 JSON 指针,以 JSON 字符串表示形式存在,或是一个有效的 相对 JSON 指针 [relative-json-pointer],该指针相对于默认上下文进行评估。
虽然可以使用 “anchor” [anchor] 关键字设置具有已知 URI 的备用上下文,但 application/json 缺乏片段标识符语法意味着通常无法使用 URI 在 JSON 实例中更改上下文。
即使在定义 JSON 指针作为片段标识符语法的“+json”媒体类型中,如果默认上下文嵌套在数组中,也无法获得默认上下文在该数组中的位置的索引,以便构建指向该相同嵌套 JSON 对象中的另一个属性的指针。这将在示例中演示。
处理此关键字的结果应该是一个 URI 片段,如果实例的媒体类型允许存在此类片段。否则,它必须是一个字符串编码的 JSON 指针。
链接的关系类型标识其语义。它是传达应用程序如何与资源交互的主要方式。
关系定义通常不依赖于媒体类型,鼓励用户利用最合适的现有接受的关系定义。
此属性的值必须是一个字符串,并且必须是 RFC 8288,第 2.1 节中定义的单个链接关系类型。
此属性是必需的。
如 RFC 4287 的第 4.2.7.2 节 [RFC4287] 中最初定义的,“self”链接表示目标 URI 标识的资源等效于链接上下文。在 JSON 超级模式中,“self”链接必须可以从实例解析,因此“hrefSchema”必须不存在。
超级模式作者应该使用“templateRequired”来确保“self”链接具有使用所需的所有实例数据。
超级模式实现必须识别,具有关系类型为“self”且具有整个当前实例文档作为其上下文的链接描述了用户代理如何与该实例文档所表示的资源进行交互。
RFC 6573 [RFC6573] 定义和注册了“item”和“collection”链接关系类型。JSON 超级模式对这些类型指示的集合资源施加了额外的语义。
实现必须识别“collection”链接的目标和“item”链接的上下文作为集合。
超媒体中一个众所周知的模式是使用集合资源来创建集合的成员,并为其提供服务器分配的 URI。如果 URI 方案指示的协议定义了一种适合于创建具有服务器分配的 URI 的资源的特定方法,那么由这些链接关系类型标识的集合资源必须不要为该方法定义与创建集合成员的语义冲突的语义。集合资源可以实现通过此类协议方法进行的项目创建,用户代理可以假设任何此类操作(如果存在)具有项目创建语义。
由于此类方法将对应于 JSON 超级模式的数据提交概念,因此链接的 “submissionSchema” [submissionSchema] 字段应该与集合项表示的模式兼容,如“item”链接的目标资源或“collection”链接的上下文资源的“self”链接所指示。
当没有注册的关系(除了“related”)适用时,鼓励用户根据RFC 8288 的 2.1.2 节 [RFC8288] 铸造自己的扩展关系类型。选择链接关系类型 URI 的最简单方法是使用已经用于标识系统主要资源的 URI 方案,或者使用人可读的、不可解析的 URI 方案,例如 RFC 4151 定义的“tag” [RFC4151]。
扩展关系类型 URI 无需可解析,即使使用允许解析的方案。
目标 URI 模板用于标识链接的目标,可能利用实例数据。此外,使用 “hrefSchema” [hrefSchema],此模板可以识别一组可能的要使用的目标资源,这些资源基于客户端输入。在 URI 模板化 [uriTemplating] 部分中介绍了使用或不使用客户端输入解析 URI 模板的完整过程。
“href”链接描述属性的值是一个模板,用于确定相关资源的目标 URI。实例属性的值必须解析为针对实例基本 URI 的 URI 引用 [RFC3986]。
此属性是必需的。
本节中的关键字在解析超模式中涉及的所有 URI 模板时使用:“base”、“anchor”和“href”。有关完整的模板解析算法,请参阅 URI 模板化 [uriTemplating] 部分。
请注意,在解析“base”模板时,解析开始的附件点是需要解析“base”模板的“href”或“anchor”关键字的附件点,而不是“base”关键字本身的附件点。
“templatePointers”链接描述属性的值必须是一个对象。对象中的每个属性值必须是一个有效的 JSON 指针 [RFC6901],或一个有效的 相对 JSON 指针 [relative-json-pointer],它相对于解析模板的链接的附件点进行评估。
对于对象中与正在解析的模板中的变量名称匹配的每个属性名称,该属性的值会调整该变量的变量解析的起始位置。与正在解析的模板中的模板变量名称不匹配的属性必须被忽略。
此关键字的值必须是一个数组,并且元素必须是唯一的。每个元素应该与链接的 URI 模板中的变量匹配,不进行百分比编码。在完成整个 URI 模板解析过程后,如果此数组中存在的任何变量都没有值,则该链接必须不被使用。
本节中的所有属性仅供参考。虽然诸如“title”和“description”之类的关键字主要用于向用户呈现链接,但那些预测链接交互或响应性质的关键字不应被视为权威性的。每当目标资源的运行时行为与 LDO 中的目标属性发生冲突时,必须尊重目标资源的运行时行为。
此属性定义链接的标题。该值必须是一个字符串。
用户代理可能会在向用户呈现链接时使用此标题。
此属性提供了超出标题中提供的额外信息。该值必须是一个字符串。虽然标题最好简短,但可以使用描述来详细说明链接的目的和用法。
用户代理可能会在向用户呈现链接时使用此描述。
此属性的值表示预期在获取此资源时返回的媒体类型 RFC 2046 [RFC2046]。此属性值可以是媒体范围,使用 RFC 7231,第 5.3.2 节 - HTTP“Accept”头 [RFC7231] 中定义的相同模式。
此属性类似于其他链接序列化格式的“type”属性。用户代理可能会使用此信息来告知他们在跟随链接之前向用户呈现的界面,但绝不能将此信息用于解释结果数据。相反,用户代理必须使用响应提供的媒体类型进行运行时解释。有关“targetMediaType”的误用详细信息,请参阅关于 “安全问题” [security] 的部分。
对于支持内容协商的协议,实现可能会选择使用 “headerSchema” [headerSchema] 中的协议特定信息来描述可能的 targetMediaType。如果协议特定信息和“targetMediaType”都存在,则“targetMediaType”的值必须与协议特定信息兼容,并且应该指示在没有内容协商的情况下将返回的媒体类型。
如果没有这样的协议特定信息可用,或者实现无法识别所涉及的协议,则该值应该被认为是“application/json”。
此属性提供了一个预期描述链接目标表示的模式。根据协议,该模式可能描述与链接执行的任何特定操作相关的请求或响应。有关此关键字如何与 HTTP 一起使用的深入讨论,请参阅 JSON 超模式和 HTTP [HTTP] 部分。
此属性的值仅供参考。它代表预期通过与目标资源交互而发现的信息,通常以协议特定控制信息或元数据的形式出现,例如作为对 HTTP HEAD 或 OPTIONS 请求的响应而返回的头。该协议由“href”URI 方案确定,但请注意,资源不能保证可以通过此类协议访问。
此属性的值应该是一个对象。此对象的键应该以控制数据字段名称的小写形式出现。每个值应该是一个数组,以便统一处理多值字段。多个值必须作为数组呈现,而不是作为单个字符串。
具有不适合作为 JSON 对象表示的控制信息的协议可以由另一种数据类型表示,例如数组。
无法理解为指示协议一部分的值必须被 JSON 超模式实现忽略。应用程序可以使用此类值,但不得假设与其他实现的互操作性。
实现不得假设此对象中考虑了所有可发现的信息。客户端应用程序必须适当地处理与此属性的值相矛盾的运行时响应。
客户端应用程序不得假设实现将自动根据此属性的值采取任何操作。
有关使用此关键字与 HTTP 和类似协议的指南,请参阅 “JSON 超模式和 HTTP” [HTTP]。
有四种方法可以使用链接的客户端输入,每种方法都由单独的链接描述对象关键字解决。在执行操作时,用户代理应该忽略与它们的语义无关的模式。
“hrefSchema”链接描述属性的值必须是一个有效的 JSON 模式。此模式用于验证用户输入或其他用户代理数据,以填充 “href” [href] 中的 URI 模板。
省略“hrefSchema”或将整个模式设置为“false”会阻止接受任何用户代理数据。
将应用于特定变量的任何子模式设置为 JSON 文字值“false”会阻止接受任何用户代理数据以用于该单个变量。
对于可以从实例数据中解析的模板变量,如果实例数据针对“hrefSchema”中的所有适用子模式有效,则必须使用它来预先填充该变量的输入数据集。
请注意,即使从实例中预先填充了数据,但“hrefSchema”中针对该变量的验证模式也不必与应用于实例数据位置的验证模式相同。这允许用户代理数据存在不同的验证规则,例如支持日期时间输入的拼写月份,但在存储中使用标准日期时间格式。
在接受输入后,可能覆盖预先填充的实例数据,结果数据集必须成功地针对“hrefSchema”的值进行验证。如果它没有,则该链接必须不被使用。如果它有效,则“URI 模板化”部分中给出的过程将继续使用此更新的数据集。
[CREF2]与“targetHints”一样,此关键字在一定程度上没有明确规定,以鼓励实验和反馈,因为我们试图在灵活性与清晰度之间取得平衡。
如果存在,此属性是用于协议特定请求头或类似控制和元数据的模式。此对象的价值必须是一个有效的 JSON 模式。该协议由“href”URI 方案确定,但请注意,资源不能保证可以通过此类协议访问。该模式仅供参考;目标资源的行为不受其存在的影响。
此关键字的目的是宣传目标资源交互功能,并向用户代理和客户端应用程序指示哪些头和头值可能有用。用户代理和客户端应用程序可以使用该模式验证相关头,但不得假设缺少头或值禁止使用。虽然模式作者可以将“additionalProperties”设置为 false,但不推荐这样做,并且不得阻止客户端应用程序或用户代理在发出请求时提供额外的头。
JSON 数据模型到头的精确映射是依赖于协议的。但是,在大多数情况下,此模式应该指定“object”类型,并且属性名称应该以控制数据字段名称的小写形式出现。有关使用此关键字与 HTTP 和类似协议的详细指南,请参阅 “JSON 超模式和 HTTP” [HTTP] 部分。
“headerSchema”适用于该协议支持的任何请求方法或命令。在生成请求时,用户代理和客户端应用程序应该忽略与该请求无关的头的模式。
在 JSON 超模式中,“targetSchema” [targetSchema] 提供了对目标资源表示的非权威描述。客户端应用程序可以使用“targetSchema”来构建用于替换或修改表示的输入,或者作为基于补丁媒体类型构建补丁文档的基本表示。
或者,如果“targetSchema”不存在,或者客户端应用程序更倾向于只使用权威信息,它可以与目标资源交互以确认或发现其表示结构。
“targetSchema”不打算描述链接操作响应,除非响应语义表明它是一个目标资源的表示。在所有情况下,响应本身指示的模式都是权威的。有关详细示例,请参见“JSON 超级模式和 HTTP” [HTTP]。
““submissionSchema” [submissionSchema] 和 “submissionMediaType” [submissionMediaType] 关键字描述了目标资源实现的处理函数的域。否则,如上所述,对于它们不相关的操作,提交模式和媒体类型将被忽略。
如果存在,此属性指示客户端应用程序和用户代理应使用哪种媒体类型格式来表示 “submissionSchema” [submissionSchema] 描述的请求有效负载。
省略此关键字的行为与 application/json 值相同。
请注意,“submissionMediaType” 和 “submissionSchema” 不限于 HTTP URI。 [CREF3]此语句可能会移到示例结束的地方。
此属性包含一个模式,该模式定义了根据“submissionMediaType” 属性进行编码并发送到目标资源以供处理的文档的可接受结构。这可以看作是描述目标资源实现的处理函数的域。
这是一个与 “targetSchema” [targetSchema] 属性不同的概念,后者描述了目标信息资源(包括用于在 PUT 请求中替换资源内容),而 “submissionSchema” 描述了用户提交的请求数据,该数据将由资源进行评估。 “submissionSchema” 旨在与请求一起使用,这些请求的有效负载不一定根据目标表示定义。
省略 “submissionSchema” 的行为与 “true” 值相同。
在高层次上,符合要求的实现将满足以下要求。这些要求中的每一个都将在各个关键字部分和关键字组概述中详细介绍。
请注意,围绕实现如何必须识别“self”、“collection” 和 “item” 链接的要求在 链接关系类型 [relationType] 部分中得到了充分的介绍,这里不再重复。
虽然这不是实现的强制格式,但测试套件中使用的输出格式总结了在使用每个链接之前需要计算的内容
不参与生成上述信息的其他 LDO 关键字将按原样包含在为测试套件生成输出时。除非与特定情况相关,否则这些字段将不再在此处讨论。
在使用链接之前,必须通过将超级模式应用于实例并查找所有适用的有效链接来发现它们。请注意,除了收集有效链接之外,还必须找到解析每个 LDO 的 URI 模板所需的任何 “base” [base] 值,并通过对实现的 URI 模板解析过程最有效的机制将其与 LDO 关联。
实现必须支持通过其附件指针或上下文指针查找链接,方法是执行查找,或者提供包含已确定这两个指针的所有链接的集合,以便用户代理可以自行实现查找。
当通过上下文指针执行查找时,附加到同一数组中的元素的链接必须以与附加它们的数组元素相同的顺序返回。
三个超级模式关键字是 URI 模板 [RFC6570]:“base”、“anchor” 和 “href”。每个关键字都分别解析为 URI 引用,然后锚点或 href URI 引用相对于基准解析(基准本身根据需要相对于较早的基准解析,每个基准首先从 URI 模板解析为 URI 引用)。
所有三个关键字都共享相同的算法来从实例数据中解析变量,该算法使用 “templatePointers” 和 “templateRequired” 关键字。当解析 “href” 时,它和解析为绝对 URI 所需的任何 “base” 模板,该算法都会修改为根据 “hrefSchema” 关键字选择性地接受用户输入。
对于每个 URI 模板 (T),以下伪代码描述了将 T 解析为 URI 引用 (R) 的算法。为了便于此算法
此算法应首先应用于 “href” 或 “anchor”,然后根据需要应用于每个后续的 “base”。顺序很重要,因为无法始终确定模板是否会解析为完整 URI 或 URI 引用。
用英语来说,高级算法是
这是高级算法的伪代码。 “T” 来自 LDO 中的 “href” 或 “anchor”,或来自包含模式中的 “base”。每个步骤的伪代码如下。“initialTemplateKeyword” 指示启动此过程的两个关键字中的哪一个(因为 “base” 始终解析以完成解析这两个关键字中的一个或另一个)。
templateData = populateDataFromInstance(T, ldo, instance) if initialTemplateKeyword == "href" and ldo.hrefSchema exists: inputData = acceptInput(ldo, instance, templateData) for varname in inputData: templateData[varname] = inputData[varname] for varname in ldo.templateRequired: if not exists templateData[varname] fatal("Missing required variable(s)") templateData = stringEncode(templateData) R = rfc6570ResolutionAlgorithm(T, templateData)
此步骤查看实例中的各个位置以查找变量值。对于每个变量
for varname in T: varname = rfc3986PercentDecode(varname) if varname in ldo.templatePointers: valuePointer = templatePointers[varname] if valuePointer is relative: valuePointer = resolveRelative(attachmentPointer, valuePointer) else valuePointer = attachmentPointer + "/" + varname value = instance.valueAt(valuePointer) if value is defined: templateData[varname] = value
此步骤相对复杂,因为它需要支持几种情况。一些变量会禁止输入,而一些则允许。一些变量将具有初始值,这些值需要在输入界面中呈现,而另一些则没有。
“InputForm” 表示任何适合的输入机制。这可能是文字 Web 表单,也可能是更具程序化的构造,例如接受特定字段和数据类型的回调函数,如果有的话,则使用给定的初始值。
form = new InputForm() for varname in T: useField = true useInitialData = true for schema in getApplicableSchemas(ldo.hrefSchema, "/" + varname): if schema is false: useField = false break if varname in templateData and not isValid(templateData[varname], schema)): useInitialData = false break if useField: if useInitialData: form.addInputFieldFor(varname, ldo.hrefSchema, templateData[varname]) else: form.addInputFieldFor(varname, ldo.hrefSchema) inputData = form.acceptInput() if not isValid(inputData, hrefSchema): fatal("Input invalid, link is not usable") return inputData:
本节很简单,将文字转换为其名称作为字符串,并将数字以最明显的方式转换为字符串,并根据需要进行百分比编码以用于 URI。
for varname in templateData: value = templateData[varname] if value is true: templateData[varname] = "true" else if value is false: temlateData[varname] = "false" else if value is null: templateData[varname] = "null" else if value is a number: templateData[varname] = bestEffortOriginalJsonString(value) else: templateData[varname] = rfc3986PercentEncode(value)
在某些软件环境中,原始 JSON 数字表示将不可用(无法区分 1.0 和 1),因此应使用任何合理的表示。模式和 API 作者应牢记这一点,并在精确表示很重要的 경우 문자열 또는 부울과 같은 다른 유형을 사용하십시오. 如果数字以字符串的形式提供作为输入,则应使用用作输入的字符串。
对于给定的链接,实现必须将所有目标属性关键字的值直接提供给用户代理。实现可以提供额外的界面来使用这些信息,如每个关键字部分所述。
对于给定的链接,实现必须将每个输入模式关键字的值直接提供给用户代理。
为了鼓励封装 URI 模板解析过程,实现可以省略仅用于构造 URI 的 LDO 关键字。但是,实现必须提供对链接关系类型的访问。
应将无法识别的关键字提供给用户代理,否则必须忽略它们。
超级模式实现应提供访问构造对目标资源的任何有效请求所需的所有信息。
LDO 可以表达执行任何链接操作所需的所有信息。本节解释了用户代理应检查哪些超级模式字段以从任何实例数据和客户端输入组合构建请求。超级模式实现本身并不期望构建和发送请求。
目标 URI 构造规则(包括用于接受输入的 “hrefSchema”)对于所有可能的请求都相同。
不携带主体有效负载的请求不需要额外的关键字支持。
以目标表示形式作为有效负载的请求应使用 “targetSchema” 和 “targetMediaType” 关键字进行输入描述和有效负载验证。如果协议允许以修改后的媒体类型(例如修补媒体类型)的表示形式作为有效负载的操作,则应通过 “targetHints” 以协议特定方式指示此类媒体类型。
对于不来自目标资源表示的有效负载的请求,**应该**使用“submissionSchema”和“submissionMediaType”关键字进行输入描述和有效负载验证。超媒体中使用的协议通常每个链接只支持一个这样的非表示操作。
通过单个超媒体协议操作传输许多具有任意不同请求结构的应用程序操作的 RPC 系统超出了 JSON 超级模式等超媒体格式的范围。
作为一种超媒体格式,JSON 超级模式关注的是描述资源,包括以足够详细的方式描述其链接,以使所有有效请求都能正常进行。它不关注直接描述这些请求的所有可能响应。
与任何超媒体系统一样,响应应该能够自我描述。在超模式的上下文中,这意味着每个响应**必须**链接其自身的超模式。虽然由目标资源表示组成的响应应该针对“targetSchema”和“targetMediaType”有效,但这些关键字只是建议性的,**必须**在响应本身与之矛盾的情况下忽略。
其他响应,包括错误响应、复杂重定向和处理状态表示,**也应该**链接到它们自己的模式并使用适当的媒体类型(例如,"application/problem+json" [RFC7807] 用于错误)。某些错误可能不会链接到模式,因为它们是由不了解超模式的中间体而不是由源生成的。
预计用户代理能够充分理解协议状态代码和响应媒体类型,以便处理常见情况,并向客户端应用程序提供足够的信息以处理特定于域的响应。
在设计时静态映射所有可能的响应及其模式超出了 JSON 超级模式的范围,但可能在基于超模式的其他 JSON 模式词汇表范围内(参见 附录 A.3)。
基于其上下文发现链接或使用链接上下文识别集合的要求在与流式解析器一起使用时带来了独特的挑战。在不处理整个模式和实例文档的情况下,无法权威地满足这些要求。
此类实现**可以**选择根据迄今为止处理的数据返回非权威答案。在提供这种方法时,实现**必须**清楚说明响应的性质,并且**必须**提供一个选项,允许阻止并等待直到所有数据都处理完毕,并且可以返回权威答案。
虽然 JSON 超级模式是一种超媒体格式,因此与协议无关,但预计它最常见的用法将在 HTTP 系统中,或者在使用与 HTTP 明确类似的协议(如 CoAP)的系统中。
本节提供了有关如何将每种常见的 HTTP 方法与链接一起使用的指导,以及集合资源如何对 HTTP POST 施加额外约束。此外,还提供了有关暗示 HTTP 响应标头值和描述与给定资源相关的可能 HTTP 请求标头的指导。
JSON 模式核心规范的第 11 节 [json-schema] 提供了有关在超媒体系统中将实例链接到其模式的指导。这可以通过网络可访问的模式来完成,或者可以简单地识别预先打包在客户端应用程序中的模式。JSON 超级模式故意不限制这种机制,虽然**建议**在可能的情况下尽可能使用核心规范中概述的技术。
链接描述对象不直接指示目标资源支持哪些操作(如 HTTP 方法)。相反,操作应该主要从链接 关系类型 [rel] 和 URI 方案推断得出。
这意味着对于每个目标资源和链接关系类型对,模式作者**应该**只定义一个 LDO。虽然可以使用“allow”与“targetHints”一起重复关系类型和目标对,并将不同的 HTTP 方法标记为允许,但这**不建议**,并且可能得不到符合规范的实现的良好支持。
本节解释了如何在一单个 LDO 中传达使用每种 HTTP 方法所需的所有信息。“targetHints”中的“allow”字段旨在简单地暗示哪些操作受支持,而不是分别定义每个操作。
但是,请注意,资源始终可以在运行时拒绝操作,例如由于授权失败,或者由于控制操作可用性的其他应用程序状态。
"targetSchema" 描述了链接目标端的资源,而“targetMediaType”定义了该资源的媒体类型。对于 HTTP 链接,“headerSchema”也可以用来描述在“Accept”请求标头中使用的有效值,这可以支持多种媒体类型或媒体范围。当两种指示目标媒体类型的方式都存在时,“targetMediaType”**应该**指示默认的表示媒体类型,而“headerSchema”中“accept”的模式**应该**包含默认值以及可以请求的任何备用媒体类型或媒体范围。
由于许多 HTTP 方法的语义是根据目标资源定义的,因此“targetSchema”用于几种 HTTP 方法的请求和/或响应。特别是,“targetSchema”建议客户端应用程序可以期待对 HTTP GET 的响应,或对“Content-Location”标头等于请求 URI 的任何响应的响应,以及客户端应用程序在 HTTP PUT 请求中替换资源时应该发送的内容。这些关联由 RFC 7231,第 4.3.1 节 - “GET”,第 4.3.4 节“PUT”,以及第 3.1.4.2 节, “Content-Location” [RFC7231] 定义。
根据 RFC 5789 [RFC5789],HTTP PATCH 的请求结构由“targetSchema”和请求媒体类型的组合决定,请求媒体类型由“Accept-Patch”标头传达,该标头可能包含在“targetHints”中。适合进行 PATCH 的媒体类型定义了表达文档更改的语法,该语法可以应用于“targetSchema”描述的表示,以确定一组语法上有效的请求有效负载。通常,验证 PATCH 请求的最简单方法是将其应用并验证结果作为正常表示。
JSON 超级模式允许资源处理任意数据,这些数据除了或代替处理目标的表示之外。这些任意数据由“submissionSchema”和“submissionMediaType”关键字描述。在 HTTP 的情况下,POST 方法是唯一处理此类数据的那个。虽然在使用 POST 与集合方面存在某些约定,但 POST 请求的语义是由目标资源而不是 HTTP 定义的。
除了协议中立的“submission*”关键字(参见 第 9.3 节 的非 HTTP 示例)之外,“Accept-Post”标头可用于指定必要的媒体类型,并且**可以**通过“targetHints”字段进行宣传。 [CREF4]如果两者都使用,会发生什么?此外,“submissionSchema”是必须支持的,而“targetHints”最多是应该支持的。但禁止在“targetHints”中使用“Accept-Post”似乎不正确。
对 POST 的成功响应,除了 201 或带有“Content-Location”设置的 200 之外,没有任何 HTTP 定义的语义。与所有 HTTP 响应一样,响应中的任何表示都应该链接到它自己的超模式,以指示如何处理它。如 附录 A.2 中所述,将超链接与所有可能的运算响应连接起来不在 JSON 超级模式的范围内。
HTTP 响应标头信息的 JSON 序列化**应该**遵循正在进行的工作 "HTTP 标头字段值的 JSON 编码" [I-D.reschke-http-jfv] 中建立的指南。该文档示例中所示的方法**应该**尽可能应用于其他类似结构的标头。
所有可能的 HTTP 方法响应的标头都共享“headerSchema”。特别是,HEAD 响应中出现的标头和 OPTIONS 响应中出现的标头都可以出现。“headerSchema”中没有区分哪种方法响应包含哪个标头。
**建议**模式作者在适用时提供以下类型 HTTP 标头的值提示
通常,在不同时间可能具有不同值的标头**不应**包含在“targetHints”中。
模式**应该**编写成描述遵循正在进行的工作 "HTTP 标头字段值的 JSON 编码" [I-D.reschke-http-jfv] 中建立的指南的 JSON 序列化。该文档示例中所示的方法**应该**尽可能应用于其他类似结构的标头。
**建议**模式作者在适用时描述以下类型 HTTP 标头的可用用法
诸如缓存控制和条件请求标头之类的标头通常由中间体而不是资源实现,因此通常没有用处进行描述。虽然资源必须提供使用条件请求所需的信息,但此类标头和相关响应的运行时处理不是特定于资源的。
在使用 HTTP 或与 HTTP 明确类似的协议(如 CoAP)时,这是通过将要创建的单个资源的表示 POST 到集合资源来完成的。识别集合和项目资源的过程在 第 6.2.3 节 中描述。
JSON 超级模式促进了 HTTP 内容协商,并允许使用主动策略和被动策略的混合。如上所述,超模式可以包含 HTTP 标头(如“Accept”、“Accept-Charset”、“Accept-Language”等)的模式,使用“headerSchema”关键字。用户代理或客户端应用程序可以使用此模式中的信息(例如,支持的语言的枚举列表),以代替发出初始请求来启动被动协商过程。
通过这种方式,设置这些标头的主动内容协商技术可以从服务器有关哪些值可能的信息中获益,类似于检查被动协商中的替代方案列表。
对于允许将模式指定为媒体类型参数的媒体类型,“Accept”值(在请求中发送或在“headerSchema”中宣传)可以包含协商表示预计符合的模式的 URI。内容协商中模式参数的一种可能用法是,如果资源在一段时间内符合多个不同的模式版本。客户端应用程序可以通过这种方式在“Accept”标头中指示它理解的版本。
本节将展示用于构建 URI 和 JSON 指针的关键字的使用方式。结果将以测试套件中使用的格式显示。[CREF6]需要发布此内容并添加链接,但对于目前审核的人来说应该比较容易理解。
大多数其他关键字要么很简单(例如“title”和“description”),要么对特定类型的输入、请求或响应进行验证,要么具有特定于协议的行为。展示 HTTP 用法的示例可以在附录 [HTTP] 中找到。
在本例中,我们将假设一个示例 API,其文档化的入口点 URI 为 https://example.com,它是一个空 JSON 对象,其中包含指向模式的链接。这里,入口点本身没有数据,仅用于提供初始的链接集。
GET https://api.example.com HTTP/1.1 200 OK Content-Type: application/json Link: <https://schema.example.com/entry> rel=describedBy {}
链接的超模式定义了 API 的基本 URI,并提供了两个链接:“about”链接指向 API 文档,“self”链接指示这是基本 URI 的模式。在本例中,基本 URI 也是入口点 URI。
{ "$id": "https://schema.example.com/entry", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "base": "https://api.example.com/", "links": [ { "rel": "self", "href": "" }, { "rel": "about", "href": "/docs" } ] }
这些是最简单的链接,只包含关系类型和“href”,没有模板变量。它们解析如下:
[ { "contextUri": "https://api.example.com", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com", "attachmentPointer": "" }, { "contextUri": "https://api.example.com", "contextPointer": "", "rel": "about", "targetUri": "https://api.example.com/docs", "attachmentPointer": "" } ]
附件指针是根指针(对于实例为空的对象,这是唯一的选择)。上下文 URI 是默认值,即请求的文档。由于 application/json 不允许片段,因此上下文指针对于完整描述上下文是必要的。它的默认行为与附件指针相同。
让我们向系统中添加“事物”,从单个事物开始。
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "base": "https://api.example.com/", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/definitions/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} } ], "definitions": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
我们的“事物”具有服务器分配的 id,这是构建“self”链接所必需的。它还有一个“data”字段,可以是任何类型。定义部分的原因将在下一个示例中说明。
请注意,“id”不是验证模式所必需的,但它是自链接所必需的。这是有道理的:一个“事物”只有在被创建并由服务器分配了 id 时才具有 URI。但是,您可以将此模式与仅包含数据字段的实例一起使用,这允许您验证您即将创建的“事物”实例。
让我们向入口点模式添加一个链接,使您可以直接跳转到特定的事物,前提是您可以提供其 id 作为输入。为了节省空间,只显示新的 LDO。与“self”和“about”不同,关于假设事物没有 IANA 注册的关系,因此使用“tag:” URI 方案 [RFC4151] 定义了扩展关系。
{ "rel": "tag:rel.example.com,2017:thing", "href": "things/{id}", "hrefSchema": { "required": ["id"], "properties": { "id": {"$ref": "thing#/definitions/id"} } }, "targetSchema": {"$ref": "thing#"} }
这里的“href”值相同,但其他所有内容都不同。请记住,实例是一个空对象,因此无法从实例数据中解析“id”。相反,它需要作为客户端输入。此 LDO 也可以使用“templateRequired”,但由于在“hrefSchema”中使用了“required”,因此严格来说并不是必需的。在没有在“hrefSchema”中标记“id”为必需的情况下提供“templateRequired”会导致错误,因为客户端输入是解析此链接的唯一可能来源。
本例介绍了使用“submission”字段来处理非表示形式输入,以及将它们与使用输入解析 URI 模板一起使用。与 HTML 表单不同的是,HTML 表单需要构建 URI 或发送有效载荷,但不能同时使用两者,JSON 超模式可以描述在同一链接上对同一操作执行的两种类型的输入。
“submissionSchema”和“submissionMediaType”字段用于描述不是目标资源表示形式的有效载荷。当与“http(s)://” URI 一起使用时,它们通常指的是 POST 请求有效载荷,如HTTP 用法附录 [HTTP] 中所述。
在本例中,我们使用“mailto:” URI,根据RFC 6068,第 3 节 [RFC6068],它没有提供任何用于检索资源的操作。它只能用于构建发送消息。由于不存在可检索、可替换或可删除的目标资源的概念,因此不使用“targetSchema”和“targetMediaType”。“submissionSchema”和“submissionMediaType”描述了非表示形式有效载荷。
我们使用“submissionMediaType”来指示多部分/备选有效载荷格式,提供同一数据的两种表示形式(HTML 和纯文本)。由于多部分/备选消息是有序序列(最后一个部分是最优选的备选方案),因此我们将序列建模为“submissionSchema”中的数组。由于每个部分本身都是一个具有媒体类型的文档,因此我们将数组中的每个项目建模为字符串,使用“contentMediaType”来指示字符串中的格式。
请注意,诸如 multipart/form-data 之类的媒体类型会将名称与每个部分相关联并且没有排序,应建模为 JSON 对象而不是数组。
请注意,某些行被换行以适应此文档的宽度限制。
{ "$id": "https://schema.example.com/interesting-stuff", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "required": ["stuffWorthEmailingAbout", "email", "title"], "properties": { "title": { "type": "string" }, "stuffWorthEmailingAbout": { "type": "string" }, "email": { "type": "string", "format": "email" }, "cc": false }, "links": [ { "rel": "author", "href": "mailto:{email}?subject={title}{&cc}", "templateRequired": ["email"], "hrefSchema": { "required": ["title"], "properties": { "title": { "type": "string" }, "cc": { "type": "string", "format": "email" }, "email": false } }, "submissionMediaType": "multipart/alternative; boundary=ab2", "submissionSchema": { "type": "array", "items": [ { "type": "string", "contentMediaType": "text/plain; charset=utf8" }, { "type": "string", "contentMediaType": "text/html" } ], "minItems": 2 } } ] }
对于 URI 参数,三个参数中的每一个都展示了不同的输入解析方式。
因此,给定从“https://api.example.com/stuff”检索到的以下实例:
{ "title": "The Awesome Thing", "stuffWorthEmailingAbout": "Lots of text here...", "email": "[email protected]" }
我们可以部分解析链接,如下所示,然后向客户端应用程序请求输入。
{ "contextUri": "https://api.example.com/stuff", "contextPointer": "", "rel": "author", "hrefInputTemplates": [ "mailto:[email protected]?subject={title}{&cc}" ], "hrefPrepopulatedInput": { "title": "The Really Awesome Thing" }, "attachmentPointer": "" }
请注意,使用“href*”关键字代替“targetUri”。这些是涵盖不同类型输入的三种可能的“targetUri”值。以下是每个值的示例:
链接是上下文资源到目标资源的类型化连接。旧的链接序列化支持“rev”关键字,它与“rel”一样接受链接关系类型,但反转了语义。这长期以来一直被弃用,因此 JSON 超模式不支持它。相反,可以使用“anchor”更改上下文 URI 来反转链接的方向。它还可以用于描述两个资源之间的链接,这两个资源都不是当前资源。
例如,有一个 IANA 注册的“up”关系,但没有“down”。在 HTTP 链接标头中,您可以将“down”实现为 "rev": "up"。
首先让我们看看如何在 HTTP 中实现它,展示一个“self”链接和两个语义相同的链接,一个是“rev:up”,另一个是使用“anchor”和“rel:up”(由于格式限制而换行)。
GET https://api.example.com/trees/1/nodes/123 HTTP/1.1 200 OK Content-Type: application/json Link: <https://api.example.com/trees/1/nodes/123> rel=self Link: <https://api.example.com/trees/1/nodes/123> rel=up anchor=<https://api.example.com/trees/1/nodes/456> Link: <https://api.example.com/trees/1/nodes/456> rev=up { "id": 123, "treeId": 1, "childIds": [456] }
请注意,“rel=up”链接的目标 URI 与“rel=self”链接相同,并将“anchor”(标识链接的上下文)设置为子级 URI。当也存在“self”链接时,工具很容易检测到这种反向链接。
以下超模式应用于上述响应中的实例,将生成相同的“self”链接和带有“anchor”的“up”链接。它还展示了使用模板化的“base” URI,以及在“templatePointers”中的绝对和相对 JSON 指针。
{ "$id": "https://schema.example.com/tree-node", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "base": "trees/{treeId}/", "properties": { "id": {"type": "integer"}, "treeId": {"type": "integer"}, "childIds": { "type": "array", "items": { "type": "integer", "links": [ { "anchor": "nodes/{thisNodeId}", "rel": "up", "href": "nodes/{childId}", "templatePointers": { "thisNodeId": "/id", "childId": "0" } } ] } } }, "links": [ { "rel": "self", "href": "nodes/{id}" } ] }
对目标(“href”)和上下文(“anchor”)URI 进行“base”模板评估是相同的。
请注意两种不同类型的 templatePointers 的使用。 “thisNodeId”映射到绝对 JSON 指针“/id”,而“childId”映射到相对指针“0”,指示当前项目的 value。绝对 JSON 指针不支持任何类型的通配符,因此无法指定类似“当前项目”的概念,除非使用相对 JSON 指针。
在许多系统中,单个资源被分组到集合中。这些集合通常还提供了一种方式来创建具有服务器分配标识符的单个项目资源。
在本例中,我们将重新使用前面部分中显示的单个事物模式。为了方便起见,这里将其重复一遍,并添加了一个“collection”链接,其中包含一个指向我们将要介绍的集合模式的“targetSchema”引用。
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "base": "https://api.example.com/", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/definitions/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} }, { "rel": "collection", "href": "/things", "targetSchema": {"$ref": "thing-collection#"}, "submissionSchema": {"$ref": "#"} } ], "definitions": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
“collection”链接对所有项目都是相同的,因此没有 URI 模板变量。 “submissionSchema”是项目本身的模式。如第 6.2.3 节 所述,如果“collection”链接支持提交机制(HTTP 中的 POST),则它必须实现项目创建语义。因此, “submissionSchema”是通过此链接创建“事物”的模式。
现在我们想要描述“事物”的集合。此模式描述了一个集合,其中每个项目表示形式与单个“事物”表示形式相同。虽然许多集合表示形式只包含项目表示形式的子集,但本例使用全部内容,以最小化所涉及的模式数量。实际的集合项目显示为对象中的数组,因为我们将在下一个示例中向对象添加更多字段。
{ "$id": "https://schema.example.com/thing-collection", "$schema": "https://json-schema.fullstack.org.cn/draft-07/hyper-schema#", "base": "https://api.example.com/", "type": "object", "required": ["elements"], "properties": { "elements": { "type": "array", "items": { "allOf": [{"$ref": "thing#"}], "links": [ { "anchorPointer": "", "rel": "item", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "thing#"} } ] } } }, "links": [ { "rel": "self", "href": "things", "targetSchema": {"$ref": "#"}, "submissionSchema": {"$ref": "thing"} } ] }
这是一个简单的包含两个元素的集合实例:
{ "elements": [ {"id": 12345, "data": {}}, {"id": 67890, "data": {}} ] }
以下是适用于此实例的所有链接,包括在引用的单个“事物”模式中定义的那些链接:
[ { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com/things", "attachmentPointer": "" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/0", "rel": "self", "targetUri": "https://api.example.com/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/1", "rel": "self", "targetUri": "https://api.example.com/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "item", "targetUri": "https://api.example.com/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "item", "targetUri": "https://api.example.com/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/0", "rel": "collection", "targetUri": "https://api.example.com/things", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/1", "rel": "collection", "targetUri": "https://api.example.com/things", "attachmentPointer": "/elements/1" } ]
在所有情况下,都显示了媒体类型为 application/json 的实例的上下文 URI,该类型不支持片段。如果实例媒体类型为 application/instance+json,该类型支持 JSON 指针片段,则上下文 URI 将包含与上下文指针字段相同的片段。对于 application/json 和没有片段的其他媒体类型,考虑上下文指针和上下文 URI 至关重要。
有三个“self”链接,一个用于集合,一个用于“elements”数组中的每个项目。项目“self”链接在单个“事物”模式中定义,该模式使用“$ref”引用。这三个链接可以通过它们的上下文或附件指针来区分。我们将在第 9.5.2 节 中重新讨论集合“self”链接的“submissionSchema”。
存在两个“item”链接,每个链接对应“elements”数组中的一个条目。与“self”链接不同,这些链接仅在集合架构中定义。它们都与对应“self”链接具有相同的目标 URI,该链接共享相同的附件指针。但是,每个链接都具有不同的上下文指针。“self”链接的上下文是“elements”中的条目,而“item”链接的上下文始终是整个集合,而与特定条目无关。
最后,存在两个“collection”链接,每个链接对应“elements”中的一个条目。在单个条目架构中,这些链接会生成以条目资源作为上下文的链接。从集合架构中引用时,上下文是“elements”数组中相关“thing”的位置,而不是该“thing”自身独立的资源 URI。
集合链接的目标 URI 相同,因为只有一个相关的集合 URI。虽然在完整的一组构建链接中计算这两个链接可能看起来没有用,但在按需构建链接时,这种安排意味着无论处理哪个“elements”条目,都会有一个“collection”链接定义。
这里我们在集合中添加分页功能。有一个“meta”部分用来保存有关当前页、下一页和上一页的信息。大部分架构与上一节中的相同,已省略。仅显示完整的新字段和新或(在主要“self”链接的情况下)已更改的链接。
{ "properties": { "elements": { ... }, "meta": { "type": "object", "properties": { "prev": {"$ref": "#/definitions/pagination"}, "current": {"$ref": "#/definitions/pagination"}, "next": {"$ref": "#/definitions/pagination"} } } }, "links": [ { "rel": "self", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/current/offset", "limit": "/meta/current/limit" }, "targetSchema": {"$ref": "#"} }, { "rel": "prev", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/prev/offset", "limit": "/meta/prev/limit" }, "targetSchema": {"$ref": "#"} }, { "rel": "next", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/next/offset", "limit": "/meta/next/limit" }, "targetSchema": {"$ref": "#"} } ], "definitions": { "pagination": { "type": "object", "properties": { "offset": { "type": "integer", "minimum": 0, "default": 0 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } } } } }
注意,“self”链接包含生成精确表示的分页查询,而不是一个通用的指向集合的链接,允许通过输入选择页面。
给定此实例
{ "elements": [ {"id": 12345, "data": {}}, {"id": 67890, "data": {}} ], "meta": { "current": { "offset": 0, "limit": 2 }, "next": { "offset": 3, "limit": 2 } } }
以下列出了适用于此实例的所有链接,这些链接在前面的示例中未出现,或者在添加分页后已更改。
[ { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com/things?offset=20,limit=2", "attachmentPointer": "" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "next", "targetUri": "https://api.example.com/things?offset=22,limit=2", "attachmentPointer": "" } ]
请注意,输出中没有“prev”链接,因为我们正在查看第一页。“meta”下缺少“prev”字段,以及“prev”链接的“templateRequired”值,意味着该链接无法与此特定实例一起使用。
让我们在入口点架构 (第 9.1 节) 中添加一个指向此集合的链接,包括分页输入,以便允许客户端应用程序直接跳转到特定页面。回想一下,入口点架构只包含链接,因此我们只显示新添加的链接
{ "rel": "tag:rel.example.com,2017:thing-collection", "href": "/things{?offset,limit}", "hrefSchema": { "$ref": "thing-collection#/definitions/pagination" }, "submissionSchema": { "$ref": "thing#" }, "targetSchema": { "$ref": "thing-collection#" } }
现在我们看到分页参数被接受为输入,因此我们可以跳转到集合中的任何页面。链接关系类型是自定义的,因为通用的“collection”链接只能与项目作为其上下文一起使用,而不能与入口点或其他资源一起使用。
当我们没有任何“thing”时,我们没有包含相关“collection”链接的资源。因此,我们无法使用“collection”链接的提交关键字来创建第一个“thing”;超架构必须针对实例进行评估。由于集合实例中的“elements”数组将为空,因此它也无法为我们提供集合链接。
但是,我们的入口点链接可以将我们带到空集合,并且我们可以使用超架构中“item”链接的存在来识别它是一个集合。由于“item”链接的上下文是集合,我们只需查找具有相同上下文的“self”链接,然后将其作为创建操作的集合进行处理。
可以推测,我们入口点架构中的自定义链接关系类型足以确保我们找到了正确的集合。识别该自定义链接关系类型的客户端应用程序可能知道它可以立即假设目标是一个集合,但通用用户代理无法做到这一点。尽管我们的示例中存在“-collection”后缀,但通用用户代理无法知道该子字符串是否表示超媒体资源集合或其他类型的集合。
一旦我们识别出“self”链接是针对正确的集合,我们就可以使用其“submissionSchema”和/或“submissionMediaType”关键字执行项目创建操作。[CREF8]如果集合未过滤且未分页,则此操作完美有效。但是,通常应将 POST 发送到将包含已创建资源的集合,并且“self”链接必须包含任何过滤器、分页或其他查询参数。即使生成的项目与过滤器不匹配或未显示在该页面内,将 POST 发送到此类“self”链接是否仍然有效?有关更多讨论,请参见 GitHub 问题 #421。 [CREF9]超架构的草案 04 定义了一个“create”链接关系,该关系将架构而不是实例作为其上下文。这与基于实例的链接模型不符,并且错误地将操作名称用于链接关系类型。但是,从架构到集合实例定义一个设计更合理的链接可能是解决此问题的一种可能方法。同样,有关更多详细信息,请参见 GitHub 问题 #421。
JSON 超架构定义了 JSON 架构核心的词汇表,并涉及到其中列出的所有安全注意事项。作为一种链接序列化格式,RFC 8288 网页链接 [RFC8288] 的安全注意事项也适用,并进行适当的调整(例如,“anchor”作为 LDO 关键字而不是 HTTP Link 标头属性)。
如 第 6.5 节 所述,所有描述目标资源的 LDO 关键字都只是建议性的,不得替代目标资源在响应操作时提供的权威信息。目标资源响应应指示其自身的超架构,该架构具有权威性。
如果目标响应中的超架构与找到当前 LDO 的超架构(通过“$id”)匹配,则目标属性可以被视为权威性的。[CREF10]需要添加一些关于通过“$id”进行欺骗的风险,但是考虑到规范的其他部分不鼓励始终重新下载链接的架构,因此风险缓解选项尚不清楚。
用户代理或客户端应用程序不得使用“targetSchema”的值来帮助解释响应链接后接收到的数据,因为这会导致“安全”数据被重新解释。
在选择如何解释数据时,服务器提供的数据类型信息(或从文件名推断,或任何其他常用方法)必须是唯一考虑因素,并且链接的“targetMediaType”属性不得使用。用户代理可以使用此信息来确定他们如何表示链接或在哪里显示链接(例如,悬停文本、在新选项卡中打开)。如果用户代理决定将链接传递给外部程序,他们应首先验证数据是否为通常传递给该外部程序的类型。
这是为了防止对“安全”数据进行重新解释,类似于对“targetSchema”采取的预防措施。
在“targetHints”中传达的协议元数据值不得视为权威性的。基于对元数据值不正确的假设而可能适用的任何协议安全注意事项都适用。
即使没有直接适用的协议安全注意事项,实现也必须准备好处理与链接的“targetHints”值不匹配的响应。
当“self”的链接关系用于表示对象的完整表示时,如果目标 URI 不等效于或不是包含具有“self”链接的目标 URI 的资源表示所使用的 URI 的子路径,则用户代理不应将该表示视为目标 URI 所表示的资源的权威性表示。[CREF11]本段中“子路径”选项的本意尚不清楚。它可能是为了允许集合中嵌入项目的表示的“self”链接(其目标 URI 通常是该集合 URI 的子路径)被视为权威性的。但是,这只是一个常见的設計慣例,似乎并未基于 RFC 3986 或任何其他关于 URI 用法的指南。有关更多讨论,请参见 GitHub 问题 #485。
感谢 Gary Court、Francis Galiegue、Kris Zyp 和 Geraint Luff 为 JSON 架构的最初草案做出的努力。
感谢 Jason Desrosiers、Daniel Perrett、Erik Wilde、Ben Hutton、Evgeny Poberezkin、Brad Bowman、Gowry Sankar、Donald Pipowitch、Dave Finlay 和 Denis Laxalde 对文档的提交和修补。
[RFC2119] | Bradner, S.,"用于 RFC 中指示需求级别的关键词",BCP 14,RFC 2119,DOI 10.17487/RFC2119,1997 年 3 月。 |
[RFC3986] | Berners-Lee, T.,Fielding, R. 和 L. Masinter,"统一资源标识符 (URI): 通用语法",STD 66,RFC 3986,DOI 10.17487/RFC3986,2005 年 1 月。 |
[RFC4287] | Nottingham, M. 和 R. Sayre,"Atom 聚合格式",RFC 4287,DOI 10.17487/RFC4287,2005 年 12 月。 |
[RFC6570] | Gregorio, J.,Fielding, R.,Hadley, M.,Nottingham, M. 和 D. Orchard,"URI 模板",RFC 6570,DOI 10.17487/RFC6570,2012 年 3 月。 |
[RFC6573] | Amundsen, M.,"条目和集合链接关系",RFC 6573,DOI 10.17487/RFC6573,2012 年 4 月。 |
[RFC6901] | Bryan, P.,Zyp, K. 和 M. Nottingham,"JavaScript 对象符号 (JSON) 指针",RFC 6901,DOI 10.17487/RFC6901,2013 年 4 月。 |
[RFC8288] | Nottingham, M.,"网页链接",RFC 8288,DOI 10.17487/RFC8288,2017 年 10 月。 |
[relative-json-pointer] | Luff, G. 和 H. Andrews,"相对 JSON 指针",Internet 草案 draft-handrews-relative-json-pointer-01,2018 年 1 月。 |
[json-schema] | Wright, A. 和 H. Andrews,"JSON 架构:描述 JSON 文档的媒体类型",Internet 草案 draft-handrews-json-schema-00,2017 年 11 月。 |
[json-schema-validation] | Wright, A.,Andrews, H. 和 G. Luff,"JSON 架构验证:用于 JSON 结构验证的词汇表",Internet 草案 draft-handrews-json-schema-validation-00,2017 年 11 月。 |
[RFC2046] | Freed, N. 和 N. Borenstein,"多用途互联网邮件扩展 (MIME) 第二部分:媒体类型",RFC 2046,DOI 10.17487/RFC2046,1996 年 11 月。 |
[RFC4151] | Kindberg, T. 和 S. Hawke,"'tag' URI 方案",RFC 4151,DOI 10.17487/RFC4151,2005 年 10 月。 |
[RFC5789] | Dusseault, L. 和 J. Snell,"HTTP 的 PATCH 方法",RFC 5789,DOI 10.17487/RFC5789,2010 年 3 月。 |
[RFC6068] | Duerst, M.,Masinter, L. 和 J. Zawinski,"'mailto' URI 方案",RFC 6068,DOI 10.17487/RFC6068,2010 年 10 月。 |
[RFC7230] | Fielding, R. 和 J. Reschke,"超文本传输协议 (HTTP/1.1):消息语法和路由",RFC 7230,DOI 10.17487/RFC7230,2014 年 6 月。 |
[RFC7231] | Fielding, R. 和 J. Reschke,"超文本传输协议 (HTTP/1.1):语义和内容",RFC 7231,DOI 10.17487/RFC7231,2014 年 6 月。 |
[RFC7807] | Nottingham, M. 和 E. Wilde,"HTTP API 的问题详细信息",RFC 7807,DOI 10.17487/RFC7807,2016 年 3 月。 |
[I-D.reschke-http-jfv] | Reschke, J.,"用于 HTTP 报头字段值的 JSON 编码",互联网草案 draft-reschke-http-jfv-06,2017 年 6 月。 |
遵循 REST 架构风格约束的超媒体 API 能够创建通用的用户代理。这种用户代理没有特定于应用程序的知识。相反,它理解预定义的媒体类型、URI 方案、协议和链接关系,通常是通过识别这些关系并协调使用实现对它们的支持的现有软件来实现的。然后,可以基于这种用户代理构建客户端应用程序,重点关注它们自己的语义和逻辑,而不是交互的机制。
超级模式一次只关注一个资源和一组相关链接。就像 Web 浏览器一次只处理一个 HTML 页面一样,它不了解该页面是否以及如何作为“网站”的一部分发挥作用,类似地,支持超级模式的用户代理一次只处理一个资源,而不了解该资源是否以及如何融入 API。
因此,超级模式适合在 API 中使用,但不适合描述 API 本身作为完整的实体。无法描述 API 范围内的概念,而不是资源和链接范围,并且这些描述超出了 JSON 超级模式的边界。
由于给定的 JSON 超级模式在单个时间点与单个资源一起使用,因此它没有版本控制的内在概念。但是,给定的资源可以随时间推移更改其使用的模式或模式,并且这些模式的 URI 可以用来指示版本控制信息。当与支持使用媒体类型参数指示模式的媒体类型一起使用时,这些版本化的模式 URI 可用于内容协商。
资源可以表明它是多个模式的实例,这允许同时支持多个兼容版本。然后,客户端应用程序可以使用它识别的超级模式,并忽略较新或较旧的版本。
由于超级模式一次表示一个资源,因此它不提供对使用链接执行的协议操作的所有可能响应的枚举。每个响应,包括错误,都被视为其自己的(可能是匿名的)资源,并且应标识其自己的超级模式,并且可选地使用适当的媒体类型,例如 RFC 7807 的“application/problem+json” [RFC7807],以允许用户代理或客户端应用程序解释除协议自身状态报告之外提供的任何信息。
可以对一组超级模式进行静态分析,而无需实例数据,以生成文档或代码等输出。但是,没有运行时实例数据,则无法访问验证和超级模式的完整功能集。
这是一个有意设计选择,以便为超媒体系统提供最大运行时灵活性。JSON 模式作为一种媒体类型允许为静态分析和内容生成建立额外的词汇表,这些内容在本规范中没有涉及。此外,如果设计时描述是一个目标,那么各个系统可能会将其使用限制为可以静态分析的子集。 [CREF12]已经提出用于 API 文档和其他目的的词汇表,欢迎在 https://github.com/json-schema-org/json-schema-vocabularies 上贡献。