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,你需要担心在客户端或你的应用程序中添加缓存。

相关文章
|
10天前
|
API 网络架构 UED
构建RESTful API的最佳实践
【8月更文挑战第54天】在数字化时代,RESTful API已成为连接不同软件系统、提供数据服务的关键桥梁。本文将深入探讨如何构建高效、可维护的RESTful API,涵盖设计原则、安全策略和性能优化等关键方面。通过具体代码示例,我们将一步步展示如何实现一个简洁、直观且功能强大的API。无论你是新手还是有经验的开发者,这篇文章都将为你提供宝贵的指导和启示。
60 33
|
6天前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
7天前
|
缓存 监控 测试技术
深入理解RESTful API设计原则与最佳实践
【9月更文挑战第26天】在数字化时代,API(应用程序编程接口)已成为连接不同软件和服务的桥梁。本文将深入浅出地介绍RESTful API的设计哲学、六大约束条件以及如何将这些原则应用到实际开发中,以实现高效、可维护和易于扩展的后端服务。通过具体实例,我们将探索如何避免常见设计陷阱,确保API设计的优雅与实用性并存。无论你是API设计的新手还是经验丰富的开发者,这篇文章都将为你提供宝贵的指导和启示。
|
11天前
|
安全 API 开发者
Web 开发新风尚!Python RESTful API 设计与实现,让你的接口更懂开发者心!
在当前的Web开发中,Python因能构建高效简洁的RESTful API而备受青睐,大大提升了开发效率和用户体验。本文将介绍RESTful API的基本原则及其在Python中的实现方法。以Flask为例,演示了如何通过不同的HTTP方法(如GET、POST、PUT、DELETE)来创建、读取、更新和删除用户信息。此示例还包括了基本的路由设置及操作,为开发者提供了清晰的API交互指南。
44 6
|
9天前
|
存储 JSON API
实战派教程!Python Web开发中RESTful API的设计哲学与实现技巧,一网打尽!
在数字化时代,Web API成为连接前后端及构建复杂应用的关键。RESTful API因简洁直观而广受欢迎。本文通过实战案例,介绍Python Web开发中的RESTful API设计哲学与技巧,包括使用Flask框架构建一个图书管理系统的API,涵盖资源定义、请求响应设计及实现示例。通过准确使用HTTP状态码、版本控制、错误处理及文档化等技巧,帮助你深入理解RESTful API的设计与实现。希望本文能助力你的API设计之旅。
31 3
|
9天前
|
存储 前端开发 API
告别繁琐,拥抱简洁!Python RESTful API 设计实战,让 API 调用如丝般顺滑!
在 Web 开发的旅程中,设计一个高效、简洁且易于使用的 RESTful API 是至关重要的。今天,我想和大家分享一次我在 Python 中进行 RESTful API 设计的实战经历,希望能给大家带来一些启发。
25 3
|
8天前
|
缓存 前端开发 API
深入浅出:RESTful API设计的最佳实践
【9月更文挑战第24天】在数字化浪潮中,API作为连接不同软件组件的桥梁,其设计质量直接影响到系统的可维护性、扩展性及用户体验。本文将通过浅显易懂的语言,结合生动的比喻和实例,带领读者深入理解RESTful API设计的核心原则与最佳实践,旨在帮助开发者构建更加健壮、灵活且用户友好的后端服务。
|
9天前
|
开发框架 JSON 缓存
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
在数字化浪潮推动下,RESTful API成为Web开发中不可或缺的部分。本文详细介绍了在Python环境下如何设计并实现高效、可扩展的RESTful API,涵盖框架选择、资源定义、HTTP方法应用及响应格式设计等内容,并提供了基于Flask的示例代码。此外,还讨论了版本控制、文档化、安全性和性能优化等最佳实践,帮助开发者实现更流畅的数据交互体验。
26 1
|
10天前
|
JSON API 开发者
惊!Python Web开发新纪元,RESTful API设计竟能如此性感撩人?
在这个Python Web开发的新纪元里,RESTful API的设计已经超越了简单的技术实现,成为了一种追求极致用户体验和开发者友好的艺术表达。通过优雅的URL设计、合理的HTTP状态码使用、清晰的错误处理、灵活的版本控制以及严格的安全性措施,我们能够让RESTful API变得更加“性感撩人”,为Web应用注入新的活力与魅力。
24 3
|
10天前
|
JSON API 网络架构
掌握RESTful API设计的艺术:从理论到实践
【9月更文挑战第22天】在数字化时代,API已成为连接不同软件和服务的桥梁。本文将引导你了解如何设计高效、可扩展且易于使用的RESTful API,涵盖从基础概念到高级最佳实践的全方位知识。我们将一起探索REST原则、HTTP方法的正确应用、资源命名策略等关键元素,并配以实际代码示例,让你能够将理论转化为实际操作,构建出既符合标准又能应对复杂业务需求的API。