RESTful API 设计的关键技巧

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

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 协议

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

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

等级 1:资源

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

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

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

回到 Slack 的例子,这是它作为 1 级 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 示例:

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

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

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

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

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

等级 3:HATEOAS

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

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

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

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

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

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

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

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

什么构成了一个好的 API

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

错误/异常处理

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

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

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

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

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

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

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

文档

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

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

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

可缓存性

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

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

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

此外,由于缓存是 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 公开时表现出色。

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

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

好的API特性

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

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

相关文章
|
2月前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
1月前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
46 12
|
2月前
|
XML JSON 缓存
深入理解RESTful API设计原则与实践
在现代软件开发中,构建高效、可扩展的应用程序接口(API)是至关重要的。本文旨在探讨RESTful API的核心设计理念,包括其基于HTTP协议的特性,以及如何在实际应用中遵循这些原则来优化API设计。我们将通过具体示例和最佳实践,展示如何创建易于理解、维护且性能优良的RESTful服务,从而提升前后端分离架构下的开发效率和用户体验。
|
2月前
|
监控 安全 API
深入浅出:构建高效RESTful API的最佳实践
在数字化时代,API已成为连接不同软件和服务的桥梁。本文将带你深入了解如何设计和维护一个高效、可扩展且安全的RESTful API。我们将从基础概念出发,逐步深入到高级技巧,让你能够掌握创建优质API的关键要素。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的指导和启示。让我们一起探索API设计的奥秘,打造出色的后端服务吧!
|
2月前
|
JSON 缓存 测试技术
构建高效RESTful API的后端实践指南####
本文将深入探讨如何设计并实现一个高效、可扩展且易于维护的RESTful API。不同于传统的摘要概述,本节将直接以行动指南的形式,列出构建RESTful API时必须遵循的核心原则与最佳实践,旨在为开发者提供一套直接可行的实施框架,快速提升API设计与开发能力。 ####
|
2月前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
2月前
|
JSON API 数据格式
探索后端开发:从零构建简易RESTful API
在数字时代的浪潮中,后端开发如同搭建一座桥梁,连接着用户界面与数据世界。本文将引导读者步入后端开发的殿堂,通过构建一个简易的RESTful API,揭示其背后的逻辑与魅力。我们将从基础概念出发,逐步深入到实际操作,不仅分享代码示例,更探讨如何思考和解决问题,让每一位读者都能在后端开发的道路上迈出坚实的一步。
|
2月前
|
JSON API 开发者
深入理解RESTful API设计原则
在数字化时代,API已成为连接不同软件应用的桥梁。本文旨在探讨RESTful API设计的基本原则和最佳实践,帮助开发者构建高效、可扩展的网络服务接口。通过解析REST架构风格的核心概念,我们将了解如何设计易于理解和使用的API,同时保证其性能和安全性。
|
2月前
|
存储 缓存 API
深入理解RESTful API设计原则
在现代软件开发中,RESTful API已成为前后端分离架构下不可或缺的通信桥梁。本文旨在探讨RESTful API的核心设计原则,包括资源导向、无状态、统一接口、以及可缓存性等,并通过实例解析如何在实际应用中遵循这些原则来构建高效、可维护的API接口。我们将深入分析每个原则背后的设计理念,提供最佳实践指导,帮助开发者优化API设计,提升系统整体性能和用户体验。
39 0
|
2月前
|
安全 测试技术 API
构建高效RESTful API:后端开发的艺术与实践####
在现代软件开发的浩瀚星空中,RESTful API如同一座桥梁,连接着前端世界的绚丽多彩与后端逻辑的深邃复杂。本文旨在探讨如何精心打造一款既高效又易于维护的RESTful API,通过深入浅出的方式,剖析其设计原则、实现技巧及最佳实践,为后端开发者提供一份实用的指南。我们不深入晦涩的理论,只聚焦于那些能够即刻提升API品质与开发效率的关键点,让你的API在众多服务中脱颖而出。 ####
35 0