在这篇文章中,我们将介绍有关HATEOAS的RESTful服务的综合文章。 超媒体是REST的秘密成分。
1.简介
在本教程的中,我们花了一些时间来刷新有关体系结构样式的基本原理的知识。 业界对状态的批判性眼光揭示了一个令人失望的事实,即它经常被完全忽略,它的关键约束之一,即作为应用程序状态引擎( )的 。
这部分的主题是 ,尤其是 。 希望我们不仅可以确信它的重要性,而且可以支持许多策略,以利用功能丰富我们的 Web服务和API。
2.这些“噪音”是什么?
众所周知,无状态是体系结构风格的强制性约束之一。 另一方面,绝大多数现实世界的Web服务和API必须处理状态管理。 看起来忽略了现代软件系统的现实和需求吗?
绝对不是, 体系结构风格承认状态管理的重要性,并以 (应用程序状态引擎)的形式提出了解决方案。 在服务器端, 的使用不仅通告资源之间的关系,而且通告可能潜在地应用于该资源的动作。 在客户端, 的存在带来了可发现性方面的下一步行动,步骤或要进行的状态转换。 理想情况下,客户端只需要知道一个URI入口点,其他所有内容都可以通过来自服务器。
显然,客户端必须足够聪明,才能以可行的方式浏览控件。 正如许多经验丰富的Web服务和API开发人员已经注意到的那样,服务器上的支持并不是那么困难,而客户端则要困难得多。
但是,在Web服务和API的上下文中, 到底是什么? 我们可以将其视为服务器随响应发送给客户端的附加元信息。 它主要包括相关资源的链接,最重要的是,适用于所讨论资源以更改其状态的上下文操作。
当我说超文本时,我的意思是信息和控件的同时呈现,从而使信息成为用户通过其获得选择和选择动作的能力。 超媒体只是文本在媒体流中包含时间锚点的扩展。 大多数研究人员已经放弃了区分。
总之,这些元数据片段告诉客户端在哪里获取更多数据或在当前上下文中哪种操作有效。 如果您觉得这很刺激,那确实是。
因此,我们讨论了什么是以及它在体系结构风格中的重要地位,现在该是我们讨论的“方式”的时候了。
3.野外的HATEOAS
仅重申我们在所说的内容, 体系结构样式要求使用 ,但未指定如何使用它。 显然,这个漏洞必须关闭,并且多年来导致了各种样式和规格的泛滥。 许多公司走得更远,提出了自己的类型,蓝图和建议。
超媒体类型是MIME媒体类型,其中包含导致应用程序流的本机超链接语义。 例如,HTML是一种超媒体类型。 XML不是。
令人惊讶的是,到目前为止,还没有一个普遍接受的标准可以遵循,以使用元素来授权Web服务和API。 一些或多或少被广泛采用,而另一些则占据相当狭窄的位置。 而且,大多数规范仍在发展之中,并且在进行中时被广告宣传。
为什么会有这么多规格,主要区别是什么? 总的来说,罪恶源于细节,更具体地说,是描述相关资源,链接和动作/操作。 如何找到最适合您的? 坦白地说,选择合适的规范会直接影响支持该规范所需的工作量。 如果是未开发的Web服务或API,您几乎可以自由选择。 相比之下,当您维护现有的Web服务或API并通过支持对其进行丰富的任务时,选择突然变得非常有限(除非您拥有完全重写的特权)。
从实现的角度来看,大多数规范都倾向于使用格式来表示资源状态和控件。 但是,有些异常值会改用协议标头。 这当然不是架构风格所决定的,而是来自现代Web服务和API的现实,它们都是面向的。
因此,这些规格是什么?
RFC 5988(网络链接)
我们将从 。 它为Web链接指定关系类型,为它们定义注册表,还定义头中带有特殊Link
头的此类链接的使用。 这是一个简单的示例:
Link: <https://rentals.jcg.com/reservations>; rel="self"; title="reservations"
可以肯定的是, Link
头(或多个Link
头)中编码的Link
可能不止一个,例如:
Link: <https://rentals.jcg.com/reservations?page=1>; rel="previous"; title="previous page",
<https://rentals.jcg.com/reservations?page=3>; rel="next"; title="next page"
是引入支持的最基本,最简单的方法。 不管是使用新的还是现有的API,都可以很容易地将其接受,但是从功能角度来看, 提供的选项集非常有限,主要是简单的关系,并且 。
哈尔
或建立使用表示超媒体控件(链接和资源)的约定。 它由于2011年创建服务和API发出文档,以便客户端可以提取适当的链接,并根据它们的关系类型在它们之间进行导航。
尽管规范仍处于 ,但其设计原理使其成为了由驱动的现代Web服务和API的最佳选择之一。
HAL的主要设计目标是通用性和简单性。 HAL可以应用于许多不同的域,并施加了满足超媒体API关键要求所需的最少结构量。
您可能会猜到,资源表示形式是格式的 ,这些使用专用的媒体类型application/hal+json
。
{
"_embedded": {
"reservations": [ {
"id": "ce5886acbb87",
"vehicle": "Volkswagen Golf 1.2 TSI",
"from": "2020-02-01",
"to": "2020-02-12",
"_links": {
"self": {
"href": "https://rentals.jcg.com/reservations/ce5886acbb87"
},
"customer": {
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
}
}
}, {
"id": "fc14e8ef90f5",
"vehicle": "BMW 325i",
"from": "2020-01-10",
"to": "2020-01-12",
"_links": {
"self": {
"href": "https://rentals.jcg.com/reservations/fc14e8ef90f5"
},
"customer": {
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
}
}
} ]
},
"_links": {
"first": {
"href": "https://rentals.jcg.com/reservations?page=0&size=10"
},
"self": {
"href": "https://rentals.jcg.com/reservations?page=0&size=10"
},
"next": {
"href": "https://rentals.jcg.com/reservations?page=1&size=10"
},
"last": {
"href": "https://rentals.jcg.com/reservations?page=1&size=10"
}
},
"page": {
"size": 10,
"totalElements": 13,
"totalPages": 2,
"number": 0
}
}
除此之外, 支持URI模板和链接关系文档。 不幸的是, 不提供任何行动支持(您可能听说过的另一个名字是 )。 试图填补空白的衍生规范之一是 。 从本质上讲,它只是的扩展,通过表达方法和属性的能力来增强它。
{
"id": "13e12765c5",
"vehicle": "Honda Civic 2020",
"from": "2020-01-01",
"to": "2020-01-05",
"_links": {
"customer": {
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
},
"self": {
"href": "https://rentals.jcg.com/reservations/13e12765c5"
}
},
"_templates": {
"default": {
"method": "put",
"properties": [ {
"name": "from",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "to",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "vehicle"
} ]
},
"delete": {
"method": "delete",
"properties": [ ]
}
}
}
请注意, 旨在仅显示针对同一资源(或URI)可用的操作。 如果是表示形式,则会为分配专用的媒体类型application/prs.hal-forms+json
。
JSON:API
是受支持的Web服务和API的最广泛支持的标准之一。 它最初是由于起草的,此后越来越受欢迎。 您可能会猜到,它仅适用于表示形式。
旨在最大程度地减少请求数量和客户端与服务器之间传输的数据量。 在不影响可读性,灵活性或可发现性的情况下实现了这种效率。
规范描述了链接,资源关系和资源修改(相当于动作)的语义。 另外,它涵盖了表示错误的方式。
{
"data": {
"id": "13e12765c5",
"type": "reservation",
"links": {
"self": "https://rentals.jcg.com/reservations/13e12765c5"
},
"attributes": {
"from": "2020-01-01",
"to": "2020-01-05",
"vehicle": "Honda Civic 2020"
},
"relationships": {
"customer": {
"links": {
"self": "https://rentals.jcg.com/reservations/13e12765c5/relationships/customer",
"related": "https://rentals.jcg.com/reservations/13e12765c5/customer"
}
}
}
}
}
使脱颖而出的原因是,考虑了诸如排序,过滤,稀疏字段集和分页之类的获取(查询)模式,这些模式也是规范的一部分。
{
"data": [
{
"id": "ce5886acbb87",
"type": "reservation",
"links": {
"self": "https://rentals.jcg.com/reservations/ce5886acbb87"
},
"attributes": {
"from": "2020-01-01",
"to": "0120-01-12",
"vehicle": "Volkswagen Golf 1.2 TSI"
},
"relationships": {
"customer": {
"links": {
"self": "https://rentals.jcg.com/reservations/ce5886acbb87/relationships/customer",
"related": "https://rentals.jcg.com/reservations/ce5886acbb87/customer"
}
}
}
},
{
"id": "fc14e8ef90f5",
"type": "reservation",
"links": {
"self": "https://rentals.jcg.com/reservations/fc14e8ef90f5"
},
"attributes": {
"from": "2020-01-10",
"to": "2020-01-12",
"vehicle": "BMW 325i"
},
"relationships": {
"customer": {
"links": {
"self": "https://rentals.jcg.com/reservations/fc14e8ef90f5/relationships/customer",
"related": "https://rentals.jcg.com/reservations/fc14e8ef90f5/customer"
}
}
}
}
],
"links": {
"first": "https://rentals.jcg.com/reservations?page[limit]=2",
"last": "https://rentals.jcg.com/reservations?page[limit]=2&page[offset]=2",
"next": "https://rentals.jcg.com/reservations?page[limit]=2&page[offset]=2"
},
"meta": {
"totalResourceCount": 3
}
}
公平地说, 具有非常简单,可读和可理解的格式。 为了实现这种折衷,它做出了一些明智的决定。 例如,您不会直接在文档中看到操作。 实际上,它们根据协议是隐式和隐式的: POST
用于创建, PATCH
用于修改, DELETE
用于删除。 这可能给客户带来一些解释上的困难,例如, ?
尽管规范的1.0版在2015年开始亮相,但它正在积极地研究之中,并被认为是一个不断发展的文档。 已正确注册了媒体类型名称application/vnd.api+json
并且还拥有自己的 。
JSON-LD
是链接数据的基于的序列化,在2014年就获得了候选正式建议的地位。它可能是最活跃的社区,而该规范的最新版本字面意思是已发布。一个月前。 设计背后的关键原则强调能够轻松集成到已经使用现有系统中,并使用语义对其进行扩充,而不会破坏已建立的联系。
可以肯定地说, 与链接数据有关的不是而是其表达信息,上下文和关系的丰富功能非常适合某些承诺。
{
"@context": {
"@vocab": "http://schema.org/"
},
"@type": "Reservation",
"id": "13e12765c5",
"vehicle": "Honda Civic 2020",
"from": "2020-01-01",
"to": "2020-01-05",
"customer": {
"@id": "https://rentals.jcg.com/customers/fed195a03e9d"
},
"@id": "https://rentals.jcg.com/reservations/13e12765c5"
}
的缺点之一是它缺乏对动作的支持。 解决了主要缺点, 是驱动的Web服务和API的词汇。
的基本思想是提供一个词汇表,该词汇表可使服务器将有效的状态转换通告给客户端。 然后,客户端可以使用此信息来构造请求,该请求将修改服务器的状态,从而实现某个所需的目标。
为了了解它是如何工作的,让我们看一下使用语义丰富的文档的快速示例。
{
"@context": {
"@vocab": "http://schema.org/",
"hydra": "http://www.w3.org/ns/hydra/core#"
},
"@type": "hydra:Collection",
"hydra:collection": [ {
"@type": "hydra:Collection",
"@id": "https://rentals.jcg.com/reservations",
"hydra:manages": {
"hydra:property": "self",
"hydra:subject": "https://rentals.jcg.com/reservations"
},
"hydra:operation": [ {
"hydra:method": "GET"
} ]
} ],
"hydra:member": [ {
"@type": "Reservation",
"vehicle": "Volkswagen Golf 1.2 TSI",
"from": "2020-02-01",
"to": "2020-02-12",
"customer": {
"@id": "https://rentals.jcg.com/customers/fed195a03e9d",
"hydra:operation": [ {
"hydra:method": "GET"
} ]
},
"@id": "https://rentals.jcg.com/reservations/ce5886acbb87",
"hydra:operation": [ {
"hydra:method": "GET"
}, {
"hydra:method": "PUT",
"hydra:expects": {
"@type": "UpdateReservation",
"hydra:supportedProperty": [ {
"hydra:property": "from"
}, {
"hydra:property": "to"
}, {
"hydra:property": "vehicle"
} ]
}
}, {
"hydra:method": "DELETE"
} ],
"id": "ce5886acbb87"
}, {
"@type": "Reservation",
"vehicle": "BMW 325i",
"from": "2020-01-10",
"to": "2020-01-12",
"customer": {
"@id": "https://rentals.jcg.com/customers/fed195a03e9d",
"hydra:operation": [ {
"hydra:method": "GET"
} ]
},
"@id": "https://rentals.jcg.com/reservations/fc14e8ef90f5",
"hydra:operation": [ {
"hydra:method": "GET"
}, {
"hydra:method": "PUT",
"hydra:expects": {
"@type": "UpdateReservation",
"hydra:supportedProperty": [ {
"hydra:property": "from"
}, {
"hydra:property": "to"
}, {
"hydra:property": "vehicle"
} ]
}
}, {
"hydra:method": "DELETE"
} ],
"id": "fc14e8ef90f5"
} ],
"hydra:totalItems": 3,
"hydra:view": {
"@type": "hydra:PartialCollectionView",
"hydra:next": "https://rentals.jcg.com/reservations?page=1&size=2",
"hydra:first": "https://rentals.jcg.com/reservations?page=0&size=2",
"hydra:last": "https://rentals.jcg.com/reservations?page=1&size=2"
}
}
再重申一次, 的最强之处是数据链接。 与结合使用,您的Web服务和API将获得成熟的功能,但是集成可能并不像人们期望的那么容易。 具有保留的媒体application/ld+json
。
警笛
由于2012年撰写,是用于表示实体的超媒体规范。 词汇表中的实体是URI可寻址资源,具有与之关联的属性,操作和可导航链接。 值得注意的是, 是专门为Web服务和API设计的,例如,动作可以直接映射到协议动词。
{
"class": [ "reservation" ],
"properties": {
"id": "13e12765c5",
"from": "2020-01-01",
"to": "2020-01-05",
"vehicle": "Honda Civic 2020"
},
"entities": [ {
"rel": [ "customer" ],
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
}, {
"class": [ "customer" ],
"rel": [ "http://schema.org/customer" ],
"properties": {
"firstName": "John",
"lastName": "Smith",
"id" : "fed195a03e9d"
}
} ],
"actions": [ {
"name": "update",
"method": "PUT",
"href": "https://rentals.jcg.com/reservations/13e12765c5",
"fields": [ {
"name": "from",
"type": "date"
}, {
"name": "to",
"type": "date"
}, {
"name": "vehicle",
"type": "text"
} ]
}, {
"name": "delete",
"method": "DELETE",
"href": "https://rentals.jcg.com/reservations/13e12765c5"
} ],
"links": [ {
"rel": [ "self" ],
"href": "https://rentals.jcg.com/reservations/13e12765c5"
} ]
}
尽管年代久远, 仍被列为进行中的作品。 它本身不像或那样流行,但是它相对简单和Web API优先语义使其成为值得考虑的选项。 表示形式的媒体类型为application/vnd.siren+json
。
集合+ JSON
由在2011年创建的规范旨在成为一种类型,旨在支持读取,编写和查询简单集合。 它在某种程度上受到和启发。 关于一个有趣事实是,它将所有内容都视为一个集合,因此,单个项目表示为一个元素的集合。
{
"collection": {
"version": "1.0",
"href": "https://rentals.jcg.com/reservations/13e12765c5",
"links": [ {
"rel": "customer",
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
} ],
"items": [ {
"href": "https://rentals.jcg.com/reservations/13e12765c5",
"data": [ {
"name": "from",
"value": "2020-01-01"
}, {
"name": "id",
"value": "13e12765c5"
}, {
"name": "to",
"value": "2020-01-05"
}, {
"name": "vehicle",
"value": "Honda Civic 2020"
} ],
"links": [ {
"rel": "customer",
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
} ]
} ],
"template": {
"data": [ {
"name": "from",
"value": ""
}, {
"name": "to",
"value": ""
}, {
"name": "vehicle",
"value": ""
} ]
}
}
}
您可能会猜到, 标准非常适合列表和集合。 它还包括对 (链接和关系)和 (操作)的支持,以及标准化的错误报告。
在某些时候, 非常流行,但是与其他替代方案相比,它实现起来可能更困难。 同样,对“万物都是收藏”的偏见是不直观的。
的媒体类型为application/vnd.collection+json
。
优步
类型描述了对简单状态转移和临时转移的支持。 它是由 (是的作者)和于2014年左右共同创建的,同时针对XML和JSON变体。
那么,作者提出另一种类型的动机是什么? ( 这则推文使推理过程更加清晰。
是高度结构化的CRUD格式。 适用于内联超媒体, 具有丰富的obj模型。 开放且极简。
文档支持链接关系,操作和错误报告机制。 该规范旨在与多种协议一起使用,但包括有关基于的实现和解释的详细指南。
{
"uber": {
"version": "1.0",
"data": [ {
"name": "customer",
"rel": [ "customer" ],
"url": "https://rentals.jcg.com/customers/fed195a03e9d"
}, {
"name": "self",
"rel": [ "self" ],
"url": "https://rentals.jcg.com/reservations/13e12765c5"
}, {
"name": "update",
"rel": [ "update" ],
"url": "https://rentals.jcg.com/reservations/13e12765c5",
"action": "replace",
"model": "from={from}&to={to}&vehicle={vehicle}"
}, {
"name": "delete",
"rel": [ "delete" ],
"url": "https://rentals.jcg.com/reservations/13e12765c5",
"action": "remove",
"model": ""
}, {
"name": "reservation",
"data": [ {
"name": "from",
"value": "2020-01-01"
}, {
"name": "id",
"value": "13e12765c5"
}, {
"name": "to",
"value": "2020-01-05"
}, {
"name": "vehicle",
"value": "Honda Civic 2020"
} ]
} ]
}
}
自2016年以来,它仍被标记为草稿(准确地说是稳定的草稿),并且没有太大变化。它没有正式注册的媒体类型,因此,如果使用表示,则必须使用application/vnd.amundsen-uber+json
。
亚哈皮
有许多鲜为人知的规范值得关注。 我们 2014年首次发布的规范( )开始,该规范 约定的列表。
最后, 只是使您的API看起来漂亮,简单且一致的首选项列表。
提供了分页,排序,部分结果和错误格式的准则列表,支持链接和关系,但不幸的是不支持操作。 它很少在野外发现并且不是真正活跃。 文档的媒体类型仅为application/json
。
石匠
是一种基于的格式,用于元素引入经典的数据表示中。 特别是,它包括用于链接和动作的元素以及标准化的错误处理。 在媒体类型注册表中注册为application/vnd.mason+json
。
离子
将自身定位为一种基于JSON的直观类型。 它涵盖了关系类型的链接以及使用 。 不幸的是,自2018年以来,该规范似乎处于休眠状态。分配给内容的媒体类型为application/ion+json
。
4. HATEOAS的成本
此时,您应该对和应用于 Web服务和API时的作用有一个很好的了解。 而且,如果您是从事典型企业项目的经验丰富的软件开发人员,您可能很难记得上一次遇到时的情况。 让我们面对现实:没有人知道如何使用 。
在体系结构的上下文中, 是必须的,但它不是免费的,并且会产生成本,有时非常重要。 不仅在实施方面,而且在前期设计方面。 在这方面,服务器相对容易,但是客户端确实很困难(充其量您可能会获得Link
标头的支持)。 实际上,这意味着即使您开发完美的 Web服务或API,其他开发人员也有很大可能选择完全不在其客户端中使用超媒体。
在本教程的这一部分中,我们讨论了许多不同的规范。 除少数例外,其中大多数带有“进行中”或“不稳定”的标签。 总的来说,这意味着仍有黑暗的水域在航行,而您最终到达那里的机会很高。 这就是为什么要持续不断地创建新规范的原因之一。 显然,每个规范需要非常不同的设计和实现工作量。 希望您的编程语言或平台生态系统已经有了一些库和框架来帮助您入门,但总的来说并非如此。
在服务器和客户端之间的数据交换方面, 可能会导致更多往返,以获取链接和关系背后的其他详细信息。 由于需要包括链接和操作,这也可能导致资源表示的大小显着增加。
您可能会问自己一个有趣的问题,即架构风格(尤其是如何与融合? 为了说明问题,请考虑一个只有两个 Customer Service
的系统,即Customer Service
和Reservation Service
。 由于客户不需要任何额外的知识或前期知识,他们如何发现有多种服务? Reservation Service
如何整合与客户相关的元素,反之亦然? 很有可能由另一层(如 ,当然这听起来并不简单。
如果此时您不惧怕和 ,请不要担心。 收益大大超过了所需的成本和精力,尤其是从长期来看。 作为对此的确认,让我们看一下简短而简单的案例研究。
5.案例研究
我们将要分析的示例应用程序是一个汽车租赁平台,该平台目前仅实现两个 Web API来管理reservations
和customers
。 与任何客户端共享的唯一知识是平台的入口点,出于演示目的,假设在伪造的URL https://rentals.jcg.com
后面有服务器。
该端点仅接受 GET
请求并返回文档(使用和 ),示例如下所示。
$ curl https://rentals.jcg.com/
{
"_links": {
"self": {
"href": "https://rentals.jcg.com/"
},
"reservations": {
"href": "https://rentals.jcg.com/reservations"
},
"customers" : {
"href": "https://rentals.jcg.com/customers"
}
}
}
一旦了解客户收到此类文档,便可以清楚地了解到可以从那里导航到两个链接: reservations
和customers
。 在这种情况下,客户对reservations
感兴趣,因此就去了那里。
$ curl -iv https://rentals.jcg.com/reservations
{
"_embedded": {
"reservations": [ {
"id": "ce5886acbb87",
"vehicle": "Volkswagen Golf 1.2 TSI",
"from": "2020-02-01",
"to": "2020-02-12",
"_links": {
"customer": {
"href": "https://rentals.jcg.com/customers/fed195a03e9d"
},
"self": {
"href": "https://rentals.jcg.com/reservations/ce5886acbb87"
}
},
"_templates": {
"default": {
"method": "put",
"properties": [ {
"name": "from",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "to",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "vehicle",
"required": true
} ]
},
"delete": {
"method": "delete",
"properties": [ ]
}
}
},
...
]
},
"_links": {
"self": {
"href": "https://rentals.jcg.com/reservations"
}
},
"_templates": {
"default": {
"method": "post",
"properties": [ {
"name": "from",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "to",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "vehicle",
"required": true
} ]
}
}
}
这次服务器返回强大的资源表示形式(为简单起见,集合中仅保留一个保留项),其中包含许多元素,因此客户端有多种选择。
例如,通过检查_templates
元素,它发现它可以通过在有效负载中提交带有from
, to
和vehicle
属性的 POST
请求来创建新的保留(因为它是 ,所以使用application/x-www-form-urlencoded
表单编码)。 您可能会注意到,尽管存在一些约束,但没有任何迹象表明from
, to
或vehicle
属性的类型(字符串?日期?数字?)是什么。
"_templates": {
"default": {
"method": "post",
"properties": [ {
"name": "from",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "to",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "vehicle",
"required": true
} ]
}
}
备选地,客户端可以通过自省相关联的_templates
元素来表达对特定保留的兴趣。
"_templates": {
"default": {
"method": "put",
"properties": [ {
"name": "from",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "to",
"regex" : "yyyy-MM-dd",
"required": true
}, {
"name": "vehicle",
"required": true
} ]
},
"delete": {
"method": "delete",
"properties": [ ]
}
}
在这种情况下,服务器提供使用 PUT
请求(完全替换语义)更新保留或使用 DELETE
请求完全删除特定保留的选择。
超媒体–结论
在本教程的这一部分中,我们讨论了和 ,这是任何 Web服务或API的组成部分。 规范的前景并非一成不变,并且在不断变化。 我们进行了很多选择,但没有遇到明显的赢家。 这样做的原因是,每一个都有不同的权衡,您需要确定哪种规范最适合您的应用程序需求。 上下文很重要,因此请认真对待。 您可能会发现有关的文章 由提供的帮助很大。
7.接下来
在本教程的下一部分中,我们将讨论文档在由支持的 Web服务和API的生命周期中的作用。
翻译自: