现在,每个人都在关注 API。API 最早开始盛行于大约 20 年前,2000 年,Roy Fielding 在他的博士论文中首次提出了 REST 这个术语。同年,Amazon、Salesforce 和 eBay 向全天下的开拓者先容了他们的 API,永久改变了我们构建软件的办法。
在 REST 之前,Roy Fielding 论文中的原则被称为“HTTP 工具模型”,随后你会明白这为何非常主要。
随着阅读的深入,你还会看到如何确定你的 API 是否成熟,好 API 的紧张品质是什么以及为何在构建 API 的时候,要看重适应性。

REST 代表表述性状态转移(Representational State Transfer),由 Roy Fielding 在他的博士论文中定义,长期以来,它便是做事 API 的圣杯。它并不是构建 API 的唯一办法,但是由于它的盛行,即便是非开拓职员也知道这种标准。
RESTful 软件有如下六种特点:
客户端-做事器端架构无状态可缓存分层系统按需编码(可选)统一接口但是,对日常利用来说,这过于理论化了。我们须要更具操作性的东西,这也便是 API 成熟度模型。
Richardson 成熟度模型该模型是由 Leonard Richardson 提出的,它将 RESTful 开拓原则结合成四个大略易行的步骤。
在模型中的位置越高,就越靠近 Roy Fielding 所定义的 RESTful 原始理念。
Level 0:POX(Plain Old XML)的泥沼Level 0 的 API 是一组大略 XML 或 JSON 的描述。在前文中,我曾经说过在 Fielding 的论文之前,RESTful 原则被称为“HTTP 工具模型”。
这是由于 HTTP 是 RESTful 开拓中最主要的组成部分。REST 要尽可能多地利用 HTTP 固有属性中的理念。
在 Level 0,没有利用任何这样的东西。我们只是构建自己的协议并把它作为一个专有层。这种架构被称为远程过程调用(Remote Procedure Call,RPC),适用于远程过程/命令。
常日我们会有一个端点,可以对它进行调用以获取一堆 XML。在这方面,一个范例的例子便是 SOAP 协议:
其余一个很好的例子便是 Slack API。它有些多样化,有多个端点,但依然是 RPC 风格的 API。它暴露了 Slack 的各种功能,中间没有附加任何特性。如下的代码展示了如何向一个特定的通道发送:
虽然按照 Richardson 的模型,这是一个 Level 0 的 API,但是这并不虞味着它是不好的。只要它是可用的,并且恰当地做事于业务需求,那它便是很棒的 API。
Level 1:资源为了构建 Level 1 的 API,我们须要找出系统中的名词并将它们通过不同的 URL 暴露出来,如下面的样例所示:
个中,“/api/books”能让我访问一个通用的图书目录,“/api/profile”能够让我访问这些书的作者的基本信息。为了获取某个资源的第一个特定实例,我可以在 URL 中添加 ID(或其他引用)。
在 URL 中还可以嵌套资源,这展示了它们因此层级构造的形式组织的。
回到 Slack 的样例,如下展示了按照 Level 1 API,它们会是什么样子的:
现在,URL 发生了变革,从原来的“/api/chat.postMessage”变成了现在的“/api/channels/general/messages”。
信息中“channel”部分从要求体转移到了 URL 中。从字面就能看出,通过利用这个 URL,我们可以预期有条发布到了“general”通道上。
Level 2:HTTP 动作Level 2 利用 HTTP 动作(verb)来添加更多的含义和意图。在这方面可用的动作比较多,我这里只用到一个根本的子集:PUT / DELETE / GET / POST。
借助这些动作,我们可以预期包含它们的 URL 有不同的行为:
POST:创建新数据PUT:更新现有的数据DELETE:移除数据GET:查找特定 id 的数据输出,获取某个资源(或全体凑集)以上面提到的“/api/books”为例:
那这里的“安全”和“幂等”又是什么意思呢?
“安全”的方法指的是永久不会改变数据的方法。REST 建议 GET 方法只能用来获取数据,以是在上面的凑集中,它是唯一一个安全的方法。不管你调用多少次基于 REST 的 GET 方法,它永久不会改变数据库中的任何东西。但是,这并不是该动作的固有特性,而是关系到你该如何实现它,以是我们须要确保它是这样运行的。所有其他的方法都会以不同的办法改变数据,不能随意利用。在 REST 中,GET 方法既是安全的,又是幂等的。
“幂等”的方法指的是多次利用不会产生不同结果的方法。按照 REST,DELETE 方法该当是幂等的,如果删除了某个资源,然后针对相同的资源再次调用 DELETE,它不会改变任何东西。资源该当早就已经消逝了。在 REST 规范中,POST 是唯一一个非幂等的方法,以是我们可以对相同的资源多次调用 POST 方法,这样我们会得到重复的资源。
我们重新看一下 Slack 样例,如果我们利用 HTTP 动作来进行更多的操作会是什么样子:
我们可以利用 POST 方法发送到通用的通道,我们也可以利用 GET 方法从通用通道获取消息。我们还可以利用 DELETE 方法和特定的 ID 删除,这里比较故意思的一点在于,并不是与特定通道关联的,以是我可以设计一个单独的 API 来删除资源。这个例子表明,设计 API 并不总是那么大略,这方面有很多可选项和权衡。
Level 3:HATEOAS还记得纯笔墨、没有任何图像的电脑游戏吗?我们只能看到一些文本,描述了你在哪里,以及接下来能干什么。为了取得进展,我们必须要输入自己的选择。在一定程度上来讲,HATEOAS 便是做这件事情的。
HATEOAS 指的是“超媒体作为运用状态引擎(Hypermedia as the Engine of Application State)”。
有了 HATEOAS 之后,当其他人利用你的 API 的时候,他们就能看到通过 API 还能做哪些其他的事情。HATEOAS 回答了“从这里出发,我还能去哪里?”的问题。
但这还不是所有的内容。HATEOAS 还可以对数据关系进行建模。我们可能会有一个关于图书的资源,并且在 URL 中没有将作者信息嵌套进来,但是我们可以包含它们的链接,如果有人对作者感兴趣的话,那么他们可以访问这些链接并探索干系的数据。
HATEOAS 不像其他成熟度模型的等级那样盛行,但是有些开拓职员确实在利用它。个中一个样例便是 Jira,如下是它们的搜索 API 的相应:
他们将链接嵌入到了其他我们可以探索的资源中,以及该 issue 的状态过渡列表。
其余一个利用 HATEOAS 的样例是 Artsy。他们的 API 严重依赖 HATEOAS,并且还利用了 JSON Plus 调用规范,按照该规范逼迫哀求利用一种分外的约定来构建链接。下面是一个分页的例子,这是利用 HATEOAS 最酷的样例之一:
我们可以供应到下一页、上一页、第一页和末了一页的链接,还可以按照须要添加其他页面的链接。这样简化了 API 的消费,由于这样不须要在客户端添加 URL 的解析逻辑,也不须要追加页码的方法。我们只须要在客户端利用已经实现构造化的链接就可以了。
好的 API 由什么组成我们已经先容完了 Richardson 模型,但这并不是实现好的 API 的全部内容。其他主要的品质还有什么呢?
缺点/非常处理我对自己利用的 API 的基本期望之一便是,须要有一种明确的办法来判断是否有缺点或非常。我想要知道要求是否得到了处理。
HTTP 有一种大略的办法来实现这一点:HTTP 状态码。
管理状态码的基本规则是:
2xx 代表统统正常3xx 代表你想要找的公主在其余一个城堡,也便是你要找的资源在其他的地方4xx 代表客户端做错了某些事情5xx 代表做事器端失落败我们的 API 至少要供应 4xx 和 5xx 状态码。有时候,5xx 是自动天生的。例如,客户端发送了一些内容到做事器端,但是这造孽的要求,而我们的校验是有缺陷的,从而导致这个问题连续在代码中实行了下去,终极导致涌现了非常,这样就会返回一个 5xx 的状态码。
如果你想要承诺利用特定的状态码,那么你会碰着“哪种状态码最适宜当前情形?”的问题。这样的问题并不总是那么随意马虎回答,我推举你去阅读声明这些状态码的 RFC,它们给出了比其他来源更广泛的阐明,并且见告了你何时利用这些状态码更得当等。幸运的是,网上有些资源可以帮助我们做出选择,比如Mozilla的HTTP状态码指南。
文档精良的 API 必须要有精良的文档。在文档方面,最大的问题在于,随着 API 的发展须要找人同步更新文档。有个更好的方案是不分开代码自更新文档。
例如,注释与代码的脱节。当代码发生变革的时候,注释依然保持不变,这样的话,注释就过期了。这乃至会比根本就没有任何注释更糟糕,由于在随后的一段韶光内,它们会供应缺点的信息。注释不会自动更新,以是开拓职员须要记得在掩护代码的时候同时掩护它们。
自更新的文档工具可以办理这个问题。在这方面,一个盛行的工具便是 Swagger,它是基于 OpenAPI 构建的工具,可以很随意马虎地描述你的 API。
Swagger 很酷的一点在于它是可实行的,以是如果你考试测验修正 API,能立即看到它的浸染和变革。
为了给 Swagger 添加自动更新功能,我们须要利用其他的插件和工具。在 Python 中,有针对大多数主流框架的插件。它们能天生 API 要求该如何组织的描述,并定义数据的输入和输出。
如果你不想要利用 Swagger,而是想利用更大略的工具,那该怎么办呢?有个盛行的替代方案是Slate。
还有一些值得推举的中间方案,如widdershins和api2html的组合,它许可我们从 Swagger 的定义中天生类似 Slate 的文档。
缓存在有些系统中,缓存可能并不是什么大问题。这样的系统可能没有很多的数据可供缓存,所有的数据都在不断地发生变革,或者系统根本没有很大的流量。
但是,在大多数情形下,缓存对付良好的性能至关主要。它与 RESTful API 密切干系,由于 HTTP 协议在缓存方面做了很多事情,比如 HTTP 头信息许可我们掌握缓存的行为。
你可能想要在客户端缓存东西,或者如果有注册表或值存储的话,那么你可能想要在运用程序中缓存数据。但是,HTTP 让我们能够基本上免费就可以得到一个很好的缓存,以是如果可能的话,请不要错过这个免费的午餐。
同时,由于缓存是 HTTP 规范的一部分,以是很多涉及 HTTP 的技能都知道如何进行缓存:浏览器原生支持缓存,客户端和做事器之间的中间技能也是如此。
API 设计的蜕变构建 API 以及当代软件最主要的部分便是适应性。如果没有适应性,开拓就会变慢,在合理的韶光发布特性就会变得更加困难,当面对末了截止韶光的时候更是如此。
“软件架构”在不同的高下文语境中有不同的含义,不过我们现在采取这个定义:
软件架构一种行为/艺术,能够避免会阻碍未来变革的决策。
记住了这一点,在设计软件的时候,当你必须要在具有相似优点的方案中做出选择时,你该当始终选择更多考虑到未来的方案。
好的实践并不是万能的。按照精确的办法构建缺点的东西并不是你想要的结果。最好采纳一种发展的心态,接管变革是不可避免的,尤其是如果你的项目要持续发展的话更是如此。
要想让你的 API 更具适应性,个中很关键的一点便是保持尽可能薄的 API 层,真正的繁芜性该当往下层转移。
API 不应该限定实现公开的 API 发布之后,它就已经完成了,是不可改变的,你就不能再去触碰它了。如果你已经有了一个设计古怪的 API,除了接管现状之外,还能做些什么呢?
你该当不断探求简化实现的方法。有时候,你可以通过一个特定的 HTTP 头信息来掌握 API 相应的格式,相对付构建其余一个叫做 v2 的新 API,这是一种更大略的办理方案。
API 只是其余一层的抽象。它们不应该决定如何实现,为了避免这种问题,我们可以采取如下几种开拓模式。
API 网关这是一种类似于门面的开拓模式。如果你要把一个单体构造拆分为一组微做事,并且希望向外部暴露一些功能的话,那么你只须要构建一个类似门面的 API 网关。
它将为不同的微做事供应一个统一的接口(这些微做事可能有不同的 API,利用不同的缺点格式等等)。
适用于前真个后端如果你必须要构建一个 API 来知足一堆不同的客户真个话,那么这可能会非常困难。针对某个客户端所作出的决策可能会影响其他客户真个功能。
按照适用于前真个后端(backend for frontend)理念,如果你有不同的客户端,它们喜好不同形式的 API,比如移动运用可能会喜好利用 GraphQL,那么就单独为它们构建吧。
只有当你的 API 是一层抽象,并且这个抽象层很薄的时候,这种办法才有效。如果它与你的数据库耦合,或者太大,具有太多的逻辑,那么就无法这样做了。
GraphQL 与 RESTful很多人都在热炒 GraphQL。它是一项新兴的技能,但是已经有了很多粉丝,以至于有些开拓者声称它将取代 REST。
只管 GraphQL 比 RESTful 要新的多,但是它们有很多相似之处。GraphQL 最大的不敷之处在于它的缓存,它必须要在客户端或运用程序中实现。现在,有内置的实现了缓存功能的客户端库(比如Apollo),但是这仍旧要比利用 HTTP 供应的险些免费的缓存功能要困难。
从技能讲,GraphQL 位于 Richardson 模型的 Level 0 层级,但是它具有良好 API 的特质。我们可能无法同时利用多个 HTTP 的功能,但是 GraphQL 的涌现便是办理这一问题的。
GraphQL 的杀手锏便是聚合不同的 API,并将它们作为一个 GraphQL API 暴露出来。
GraphQL 在处理数据抓取不敷和数据过量抓取方面有很好的效果,而这些问题是 REST API 很难进行管理的。这两个问题都与性能有关,如果数据抓取不敷,那解释你没有高效地利用 API,以是必须要进行大量的调用。如果数据过量抓取的话,那么 API 调用的数据传输会比必要的数据传输更大,这是对带宽的一种摧残浪费蹂躏。
小结借助 REST 与 GraphQL 的比较,我们能够总结出一个好的 API 最主要的品质。
好的 API 的特性
我们须要一个清晰的数据表述办法:RESTful 以资源的办法供应了表述。我们须要有一种办法显示有哪些可用的操作:RESTful 通过组合伙源和 HTTP 动作实现这一点。我们须要有一种办法来确认是否存在缺点/非常:HTTP 状态码可以实现这一点,可能还会包含阐述它们的相应信息。最好能够供应 API 创造和导航的功能:在 RESTful 中,HATEOAS 卖力实现这一点。有好的文档是非常主要的:在这方面,可实行、自更新的文档可以办理这个问题,这超出了 RESTful 规范的范围。末了,但同样主要的是,精良的 API 该当具有缓存功能,除非你的特定情形认为它是不必要的。
REST 和 GraphQL 之间最大的差异是它们处理缓存性的办法。当我们利用 REST 办法构建 API 的时候,我们基本上可以免费得到 HTTP 的缓存功能。如果选择 GraphQL 的话,你须要自行卖力为客户端或运用程序添加缓存。
原文链接:
https://www.stxnext.com/blog/how-to-build-a-good-api-that-wont-embarrass-you
延伸阅读:
4种主流的API架构风格比拟-InfoQ关注我并转发此篇文章,即可得到学习资料~若想理解更多,也可移步InfoQ官网,获取InfoQ最新资讯~