RESTful API 设计教程

liam
• 阅读 172

RESTful 架构基础

REST,代表表现层状态转移(Representational State Transfer),长久以来一直是 API 服务的圣杯,最初由 Roy Fielding 在其博士论文中定义。尽管它不是构建 API 的唯一方法,但由于其广泛的普及,即使是非开发者也对其有所了解。

RESTful 软件具有六个关键特征:

  • 客户端-服务器架构
  • 无状态性
  • 可缓存性
  • 分层系统
  • 按需代码(可选)
  • 统一接口

但这些还太理论化了,我们需要一些更具操作性的内容,那就是 API 成熟度模型。

Richardson 成熟度模型

由 Leonard Richardson 开发,该模型将 RESTful 开发的原则合并为四个易于遵循的步骤。

等级 0:POX 的沼泽

一个 0 级 API 是一组简单的 XML 或 JSON 描述。在介绍中,我提到在 Fielding 的论文之前,RESTful 原则被称为 “HTTP 对象模型”。

这是因为 HTTP 协议是 RESTful 开发的最重要部分。REST 围绕尽可能多地使用 HTTP 的固有属性的理念展开。

在 0 级,你根本不使用这些东西。你只是构建自己的协议并将其用作专有层。这种架构被称为远程过程调用(RPC),它非常适合远程过程/命令。

你通常有一个端点来接收一堆 XML 数据。例如 SOAP 协议

RESTful API 设计教程

另一个很好的例子是 Slack API。它稍微多样化一些,有几个端点,但它仍然是 RPC 风格的 API。它暴露了 Slack 的各种功能,中间没有增加任何功能。以下代码允许你向特定频道发布消息。

RESTful API 设计教程

尽管它是根据 Richardson 的模型是 0 级 API,但这并不意味着它不好。只要它可用并能正确服务于业务需求,它就是一个很棒的 API。

等级 1:资源

要构建一个 1 级 API,你需要在系统中找到名词,并通过不同的 URL 暴露它们,如下例所示。

RESTful API 设计教程

/api/books 将带我进入通用书籍目录。/api/profile 将带我进入这些书的作者的个人资料(如果只有一个的话)。要获取资源的第一个具体实例,我在 URL 中添加 ID(或其他引用)。

我还可以在 URLs 中嵌套资源,并显示它们是如何层次化组织的。

回到 Slack 的例子,这是它作为 1 级 API 的样子:

RESTful API 设计教程

URL 发生了变化;现在我们有了/api/channels/general/messages 代替/api/chat.postMessage。

“channel”部分的信息已从正文移到URL中。这确实表明使用这个 API,你可以期待将消息发布到 general 频道。

等级 2:HTTP 动词

一个 2 级 API 利用 HTTP 动词添加更多的含义和意图。这些动词有很多,我只使用一小部分基本的:PUT / DELETE / GET / POST。

使用这些动词,我们期望含有它们的 URLs 展现不同的行为:

  • POST—创建新数据
  • PUT—更新现有数据
  • DELETE—移除数据
  • GET—寻找特定 id 的数据输出,或获取资源(或整个集合)

或者,使用之前的 /api/books 示例:

RESTful API 设计教程

“安全”和“幂等”的含义是什么?

“安全”的方法是不会改变数据的方法。REST 建议 GET 只应该用来获取数据,因此它是上述集合中唯一的安全方法。不论你调用一个基于 REST 的 GET 方法多少次,它都不应该在数据库中改变任何东西。但这并不固有于动词——这取决于你如何实现它,所以你需要确保这一点。所有其他方法将以不同的方式改变数据,不能随机使用。在 REST 中,GET 既是安全的也是幂等的。

一个“幂等”的方法是在多次使用中不会产生不同结果的方法。根据 REST 的说法,DELETE 应该是幂等的——如果你一次删除一个资源,然后再次调用 DELETE 该资源,它不应改变任何东西。资源应该已经消失了。POST 是 REST 规范中唯一的非幂等方法,所以你可以多次 POST 同一个资源,你会得到重复项。

让我们重新审视 Slack 的例子,看看如果我们在其中使用 HTTP 动诖进行更多操作会是什么样子。

RESTful API 设计教程

我们可以使用 POST 向 general 频道发送消息。我们可以使用 GET 从 general 频道获取消息。我们可以使用 DELETE 删除具有特定 ID 的消息——这变得有趣了,因为消息不与特定频道绑定,所以我可能需要设计一个单独的 API 来移除消息。这个例子展示了设计 API 并不总是容易的;有很多选择和权衡要做。

等级 3:HATEOAS

还记得只有文本的计算机游戏,没有任何图形吗?你只有很多描述你在哪里,以及你接下来能做什么的文本。要进展,你必须键入你的选择。HATEOAS 就有点像这样。

RESTful API 设计教程

HATEOAS 代表“应用程序状态的超媒体引擎”(Hypermedia as the Engine of Application State)

当你有了 HATEOAS,每当有人使用你的API时,他们可以看到他们还可以用它做什么。HATEOAS 回答了“我接下来可以去哪里?”的问题。

RESTful API 设计教程

但这还不是全部。HATEOAS 还可以对数据关系进行建模。我们可以拥有一个资源,URL 中不嵌套作者,但我们可以发布链接,所以如果有人对作者感兴趣,他们可以去那里探索。

这不像成熟度模型的其他级别那样流行,但有些开发者使用它。例如 Jira,下面是他们搜索 API 的一部分:

RESTful API 设计教程

他们嵌套了你可以探索的其他资源的链接,以及这个问题的转换列表。他们的 API 很有趣,因为它在顶部有一个“扩展”参数。它允许你选择你不想要链接的字段,而是选择完整内容。

使用 HATEOAS 的另一个例子是 Artsy。他们的 API 严重依赖 HATEOAS。他们还使用 JSON Plus 调用规范,这为链接结构制定了特殊的约定。下面是使用 HATEOAS 进行分页的一个例子,这是使用 HATEOAS 的最酷的例子之一。

RESTful API 设计教程

你可以提供指向下一个、上一个、第一个、最后一个页面的链接,以及你认为必要的其他页面的链接。这简化了 API 的使用,因为你不需要在客户端添加URL解析逻辑,或者添加分页号的方式。你只需得到已经结构化好的链接的客户端就可以使用了。

什么构成了一个好的 API

到此为止 Richardson 的模型,但这并不是构成好API的全部。其他重要的质量是什么呢?

错误/异常处理

我期待从我使用的 API 中得到的一个基本的东西是,需要有一个明显的方式来告诉我是否有错误或异常。我需要知道我的请求是否已处理。

瞧,HTTP 还有一种简单的方式来做到这一点:HTTP 状态码。

控制状态代码的基本规则是:

  • 2xx 表示正常
  • 3xx 表示你要找的公主在另一个城堡——你要找的资源在另一个地方
  • 4xx 表示客户端做了一些错误的事情
  • 5xx 表示服务器失败
  • 500 内部服务器错误 - 小猫咪梗

RESTful API 设计教程

至少,你的 API 应该提供 4xx 和 5xx 状态码。5xx 有时是自动生成的。例如,客户端向服务器发送某些东西,它是一个无效请求,验证有缺陷,问题沿着代码下发,我们有一个异常——它将返回一个 5xx状 态码。

如果你想要致力于使用特定的状态码,你会发现自己在想,“哪个代码最适合这种情况?”这个问题并不总是容易回答。

我建议你去查阅 RFC,它规定了这些状态码,比其他来源提供更广泛的解释,告诉你这些代码什么时候合适等等。幸运的是,有几个在线资源可以帮助你选择,比如 Mozilla 的 HTTP 状态码指南。

文档

伟大的 API 拥有伟大的文档。文档的最大问题通常是找人来更新它,随着 API 的增长。一个很好的选择是自我更新的文档,它与代码没有脱节。

例如,注释与代码无关。代码改变时,注释保持不变,变得过时。它们可能比没有注释还糟糕,因为过一段时间后它们将提供错误的信息。注释不会自动更新,所以开发者需要记得与代码一起维护它们。

自我更新文档工具解决了这个问题。一个流行的工具 Apifox 可以高效的帮助你解决问题。

RESTful API 设计教程

可缓存性

在某些系统中,可缓存性可能不是大问题。你可能没有很多可以缓存的数据,一切都在不断变化,或者你可能没有很多流量。

但在大多数情况下,可缓存性对于良好的性能至关重要。它与 RESTful API 相关,因为HTTP协议与缓存有很多关系,例如 HTTP 头允许你控制缓存行为。

你可能希望在客户端缓存东西,或者在你的应用程序中缓存,如果你有一个注册表或值存储来保存数据。但 HTTP 允许你几乎免费获得良好的缓存,所以如果可能的话——不要错过免费的午餐。

RESTful API 设计教程

此外,由于缓存是 HTTP 规范的一部分,很多参与 HTTP 的东西都会知道如何缓存东西:浏览器,它们天生支持缓存,以及你和客户端之间的其他中间服务器。

进化的 API 设计

构建 API 和现代软件的最重要部分是适应性。没有适应性,开发时间会减慢,尤其是在面对截止日期时,推出功能变得更加困难。

“软件架构”在不同的上下文中意味着不同的东西,但就目前而言,让我们采纳这个定义:

软件架构:避开阻碍未来变更的决策的行为/艺术。

考虑到这一点,当你设计你的软件并必须在具有相似好处的选项之间选择时,你应始终选择更具未来性的那一个。

好的实践并不是一切。以正确的方式构建错误的东西并不是你想要的。更好的是采纳成长的心态并接受变化是不可避免的,尤其是如果你的项目将继续增长的话。

为了让您的 API 更具适应性,其中一个关键做法是保持API层的轻便。真正的复杂性应该下放。

API 不应该决定实现

一旦你发布一个公共 API,它就是固定的,你不能更改它。但如果你别无选择,只能承诺一个设计得不够好的 API 怎么办?

你应该始终寻找简化实现的方法。有时,用一个特殊的 HTTP 头来控制你的 API 的响应格式可能是一个比构建另一个 API 并称之为 v2 更简洁的解决方案。

API 只是另一层抽象。它们不应该决定实现。有几种开发模式可以帮助你避免这个问题。

API 网关

这是一种外观模式开发模式。如果你将一个单体分解成一堆微服务,并想向世界公开一些功能,你只需建立一个 API 网关,它就像一个外观一样。

它将为不同的微服务(可能具有不同的 API,使用不同的错误格式等)提供一个统一的接口。

针对前端的后端

如果你需要构建一个 API 来满足几种不同的客户端,这可能会很困难。为一个客户做出的决策会影响其他客户的功能。

针对前端的后端说——如果你有不同的客户喜欢不同的 API,比如移动应用喜欢 GraphQL,那就为他们建立 API。

这只有在你的 API 是一个抽象层,并且很薄的情况下才有效。如果它与你的数据库耦合,或者太大,逻辑太多,你就无法做到这一点。

GraphQL 与 RESTful

GraphQL 有很多炒作。它是新来的,但已经吸引了许多粉丝。以至于一些开发者声称它将取代 REST。

尽管 GraphQL 相对于 RESTful 规范来说较新,但它们有很多相似之处。GraphQL 的最大缺点是可缓存性——它必须在客户端或应用程序中实现。有客户端库具备内建的缓存能力(如 Apollo),但这比利用 HTTP 提供的几乎免费的缓存能力更难。

技术上讲,GraphQL 处于 Richardson 模型的 0 级,但它具有良好 API 的特性。你可能无法使用几项 HTTP 功能,但 GraphQL 旨在解决特定问题。

GraphQL 在合并不同API并将它们作为一个 GraphQL API 公开时表现出色。

RESTful API 设计教程

GraphQL 在处理欠抓取和过度抓取方面表现出色,这是 REST API 可能难以管理的问题。这两者都与性能相关——如果你欠抓取,你没有有效地使用 API 调用,所以你必须进行很多调用。当你过度抓取时,你的调用导致的数据传输比必要的更大,这是带宽浪费。

REST 与 GraphQL 的比较是一个很好的过渡,总结了一个好 API 的最重要特征。

RESTful API 设计教程

好的API特性

  • 你需要清晰表示数据——RESTful 通过资源的形式为你提供这一点。
  • 你需要展示哪些操作可用——RESTful 通过结合资源与 HTTP 动词做到这一点。
  • 需要有一种确认是否存在错误/异常的方法——HTTP 状态码可以做到这一点,可能还有解释它们的响应。
  • 有可发现性和导航的可能性很好——在 RESTful 中,HATEOAS 负责这一点。
  • 拥有出色的文档很重要——在这种情况下,可执行的、自更新的文档可以处理这个问题,这超出了 RESTful 规范的范畴。
  • 最后但同样重要的是——伟大的 API 应该具备可缓存性,除非你的特定情况表明这不是必需的。

REST 与 GraphQL 之间最大的区别是它们处理缓存的方式。当你按照 REST 方式构建你的 API 时,你几乎可以免费获得 HTTP 缓存。如果你选择 GraphQL,你需要担心在客户端或你的应用程序中添加缓存。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
2年前
RestFul服务介绍
1\.REST介绍REpresentationalStateTransfer(REST)是一种架构原则,其中将web服务视为资源,可以由其URL唯一标识。RESTfulWeb服务的关键特点是明确使用HTTP方法来表示不同的操作的调用。REST的基本设计原则对典型CRUD操作使用HTTP协议方法:POST创建资源GET
Wesley13 Wesley13
2年前
(三)Kubernetes 快速入门
 Kubernetes的核心对象APIServer提供了RESTful风格的编程接口,其管理的资源是KubernetesAPI中的端点,用于存储某种API对象的集合,例如,内置Pod资源是包含了所有Pod对象的集合。资源对象是用于表现集群状态的实体,常用于描述应于哪个节点进行容器化应用、需
Stella981 Stella981
2年前
RESTful API 设计最佳实践
WebAPI近几年变得越来越火,而简洁的API设计在多后端系统交互应用中也变得尤为重要。通常,会使用RESTfulAPI来作为我们的WebAPI。本文介绍了几种简洁RESTfulAPI设计的最佳实践。使用的名词而不是动词使用名词来定义接口!(https://static.oschina.net/uploads
Stella981 Stella981
2年前
RESTful API教程:学习关键的Web服务设计原则
!(https://oscimg.oschina.net/oscnet/3c978d628a8749d698f49982d5f724a8.jpg)本文为翻译发表,转载需要注明来自公众号EAWorld。作者:CameronMcKenzie译者:白小白原题:RESTfulAPIstutorial:Lear
Stella981 Stella981
2年前
RESTful API 设计实践
RESTfulAPI为网络应用程序设计提供了一套统一、合理的风格。它只是一种风格,而不是标准,所以也就没有一套统一的标准去规范化这些设计,本文从实践的角度出发,讨论RESTfulAPI设计上的一些细节,探讨如何设计出一套好用、合理、精炼的API。版本按照RESTfulAPI的风格,不同版本的API应该是同一种资源的不同表现
Stella981 Stella981
2年前
RESTful API 设计指南
  作者: 阮一峰(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.ruanyifeng.com%2F)  网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。  因此,必须有一种统一的机制,方便不同
Wesley13 Wesley13
2年前
API设计的一点思路
API是模块或者子系统之间交互的接口定义。好的系统架构离不开好的API设计,而一个设计不够完善的API则注定会导致系统的后续发展和维护非常困难。以下谈一点API设计的原则。业务层业务语义简单明确一个接口或者说一个api,必定是为外部使用者服务的,因此必须具有明确的业务/使用意图。api的从命名到定义,都必须围绕着这个意图来进行
API 小达人 API 小达人
1年前
为什么要 API 优先?
最近关于APIFirst(API优先)作为设计和开发方法的讨论很多,虽然通向APIFirst的途径有很多,但通常推动APIFirst的一般都是API架构师、API设计师和API平台负责人等,很好理解,因为他们对组织中API的效率、互操作性和质量最感兴趣。
小万哥 小万哥
8个月前
全面的开发者文档和用户目标解析:API 文档指南和开发者旅程
开发者文档开发者文档,也称为API文档,是一种专门针对软件开发人员的技术写作形式。这种类型的文档通常包括API的技术规范、代码注释、软件设计和架构以及软件开发中涉及的其他详细技术描述。开发者文档是开发人员的重要工具,因为它提供了使用和集成特定软件、库或AP
liam liam
2个月前
API 设计:基础知识与最佳实践指南
在这篇深入探讨中,我们将从基础开始,逐步介绍API设计,并探讨定义卓越API的最佳实践。作为一名开发者,你可能已经熟悉了许多这些概念,但我将提供详细解释,以加深你的理解。API设计:电子商务示例让我们考虑一个像这样的电子商务平台的API,如果你不熟悉,Sh