伟大的 Web API 设计的 5 条黄金法则
已发表: 2022-03-11有没有发现自己想知道“他们在想什么?” 当通过其 API 集成 Web 服务时? 如果没有,你比我幸运得多。
任何软件开发人员都知道让项目演变成意大利面条式代码是多么容易,而且 Web API 也同样容易导致网络错综复杂。 但它不需要是这样的。 事实上,有可能设计出人们会真正喜欢使用并且您也会喜欢创建的优秀Web API。 但是怎么做? 这个问题的答案就是这篇文章的全部内容。
看法
大多数情况下,当您构建解决方案时,您是在为非程序员或一般技术不成熟的最终用户进行设计。 你为他们提供了一个图形界面,如果你的工作做得很好,你已经从他们那里收集到了一个很好的想法,即他们需要这个界面来做什么。
但 API 开发不同。 您正在为程序员设计一个界面,可能甚至不知道他们是谁。 不管他们是谁,他们都会有技术成熟度(或者至少会认为他们有技术成熟度)来指出你软件中的每一个小缺陷。 您的用户可能会像您对他们的 API 一样批评您的 API,并且会非常喜欢批评它。
顺便说一句,其中有部分讽刺意味。 如果有人应该了解如何制作易于使用的 Web API,那就是您。 毕竟,你和你的 API 的用户一样是一名软件工程师,所以你分享他们的观点。 不是吗?
好吧,虽然您当然了解他们的观点,但您不一定同意他们的观点。 当您开发或增强 API 时,您拥有 API设计者的视角,而他们拥有 API用户的视角。
API 设计人员通常关注诸如“此服务需要做什么?”之类的问题。 或“这项服务需要提供什么?” ,而API 用户则专注于“我如何使用这个 API 来做我需要的事情?” ,或者更准确地说, “我怎样才能花最少的精力从这个 API 中得到我需要的东西?” .
这些不同的问题导致了两种截然不同的观点。 因此,设计出色API 的必要先决条件是将您的视角从 API 设计者的视角转变为 API 用户的视角。 换句话说,不断问自己如果你是自己的用户自然会问的问题。 与其考虑你的 API能做什么,不如想想它可能需要或想要使用的不同方式,然后专注于让 API 的用户尽可能轻松地完成这些任务。
虽然这听起来简单明了,但令人惊讶的是 API 很少以这种方式设计。 想想你在职业生涯中遇到的 API。 它们在设计时似乎考虑到这种观点的频率如何? Web API 设计可能具有挑战性。
话虽如此,让我们继续讨论设计出色 Web API 的 5 条黄金法则,即:
- 文档
- 稳定性和一致性
- 灵活性
- 安全
- 易于采用
规则 1:文档
文档。 是的,我从这里开始。
你讨厌文档吗? 好吧,我可以理解,但戴上你的“用户视角”帽子,我敢打赌,除了编写文档之外,你最讨厌的一件事就是不得不尝试使用未记录的 API。 我休息一下。
底线是,如果您希望任何人使用您的 API,文档是必不可少的。 你只需要把这件事做好。 这是用户首先看到的东西,所以在某些方面它就像礼品包装。 呈现得好,人们更有可能使用你的 API 并忍受任何特质。
那么我们如何编写好的文档呢?
相对简单的部分是记录 API 方法本身; 即,示例请求和响应,以及两者中每个元素的描述。 幸运的是,有越来越多的软件工具可以促进和简化生成文档的任务。 或者您可以自己编写一些东西来内省您的 API、端点和函数,并为您生成相应的文档。
但是,优秀文档与充足文档的区别在于包含使用示例,理想情况下,还包含教程。 这可以帮助用户了解您的 API 以及从哪里开始。 它引导他们并帮助他们将您的 API 加载到他们的大脑中。
例如,如果 Twilio 的开发人员要列出对其 API 的每个类、每个方法和每个可能的响应,但没有提及您可以通过以下方式发送 SMS、跟踪呼叫或购买电话号码他们的 API,API 用户需要很长时间才能找到该信息并有凝聚力地理解它。 你能想象对一棵巨大的类和方法树进行排序,而不知道它们的用途,除了它们的名字吗? 听起来很可怕吧? 但这正是许多 API 提供者所做的,因此他们的 API 对任何人来说都是不透明的,除了他们自己。 Rackspace CloudFiles 开发人员和 API 指南就是这样一个例子; 除非您已经了解他们在做什么以及他们在提供什么,否则很难掌握方向。
因此,编写简明的教程,帮助开发人员快速启动和运行,至少包含他们正在尝试做的事情的框架,然后将他们指向更详细、完整记录的功能列表的方向,以便他们可以扩展关于他们所拥有的。
完成文档后,请务必验证它对您以外的其他人是否有意义。 将其发送给您网络中的其他开发人员,除了将他们指向文档外,不给他们任何指示,并要求他们按照教程或在大约 15 分钟内构建一些非常基本的东西。 如果他们无法在 15 分钟内与您的 API 进行基本集成,那么您还有更多工作要做。
有关优秀且详细的文档的一些值得注意的示例,请查看 Twilio、Django 和 MailChimp。 这些产品都不一定是其市场中最好的(尽管它们都是好产品),但它们确实通过在其市场中提供一些最好的文档来区分自己,这无疑促进了它们的广泛接受和市场份额。
规则 2:稳定性和一致性
如果您曾经使用过 Facebook 的 API,您就会知道他们弃用和完全重写其 API 的频率。 无论您多么尊重他们的黑客文化或他们的产品,他们的观点都不是对开发人员友好的。 他们仍然成功的原因是他们有十亿用户,而不是因为他们的 API 很棒。
但是您可能没有如此庞大的用户群和市场份额,因此您将需要一个波动性小得多的 API,让旧版本在相当长的一段时间内保持运行和支持。 甚至可能几年。 因此,为此,这里有一些提示和技巧。
例如,假设您的 API 可通过 URL http://myapisite.com/api/widgets
访问,并以 JSON 格式提供其响应。 虽然乍一看这似乎很好,但当您需要修改 JSON 响应的格式时会发生什么? 已经与你融为一体的每个人都会崩溃。 哎呀。
所以提前做一些计划,从一开始就对你的 API 进行版本化,明确地将版本号合并到 URL 中(例如, http://myapisite.com/api/widgets?version=1
://myapisite.com/api/widgets?version=1 或http://myapisite.com/api/widgets/v1
),这样人们就可以依赖版本 1 工作,并且可以在准备好升级到任何后续版本时进行升级。 如果您需要在某个时候逐步淘汰之前的版本,请继续,但要给予充分的通知并提供某种过渡计划。
一个好的 URL 方案将在 URL 中包含主要版本。 对输出格式或支持的数据类型的任何更改都应导致升级到新的主要版本。 通常,如果您所做的只是将键或节点添加到输出中,则保持相同的版本是可以接受的,但为了安全起见,任何时候输出更改时,都会更新一个版本。
除了随着时间的推移保持稳定之外,API 还需要在内部保持一致。 我见过许多 API 会根据所使用的端点更改参数名称或 POSTing 数据的方法。 相反,您应该在 API 中全局处理通用参数,并使用继承或共享架构在整个 API 中一致地重用相同的命名约定和数据处理。
最后,您需要记录并发布变更日志以显示 API 版本之间的差异,以便用户准确了解如何升级。
规则 3:灵活性
垃圾进垃圾出 (GIGO) 是大多数程序员众所周知的口头禅。 应用于 Web API 设计时,该指导原则倾向于规定一种相当严格的请求验证方法。 听起来不错,对吧? 没有混乱,没有问题。

然而,与所有事情一样,需要有一些平衡。 由于无法预测用户希望使用您的服务的每一种方式,并且由于并非每个客户端平台都是一致的(即,并非每个平台都具有非常好的 JSON 支持、体面的 OAuth 库等),因此最好对于您的输入和输出约束,至少具有一定程度的灵活性或容忍度。
例如,许多 API 将支持各种输出格式,如 JSON、YAML、XML 等。 al.,但仅支持在 URL 本身中指定格式。 本着保持灵活性的精神,您可以允许在 URL 中指定它(例如/api/v1/widgets.json
),或者您也可以读取并识别Accept: application/json
HTTP 标头,或支持查询字符串变量,例如?format=JSON
等。
当我们这样做的时候,为什么不允许指定的格式不区分大小写,所以用户也可以指定?format=json
呢? 这是减轻 API 用户不必要的挫败感的经典示例。
另一个例子是允许输入变量的不同方式。 因此,就像您有多种输出格式一样,也允许多种输入格式(例如,纯 POST 变量、JSON、XML 等)。 您至少应该支持标准 POST 变量,并且许多现代应用程序也支持 JSON,因此这两个是一个很好的起点。
这里的重点是你不应该假设每个人都分享你的技术偏好。 通过对其他 API 的工作原理进行一些研究,并通过与其他开发人员的对话,您可以收集其他有用的有价值的替代方案并将它们包含在您的 API 中。
规则 4:安全
安全性显然是构建到您的 Web 服务中的最重要的事情之一,但是如此多的开发人员使得它非常难以使用。 作为 API 提供者,您应该提供在访问 API 时如何进行身份验证和授权的可用示例。 这应该不是最终用户花费数小时处理的难题。 让他们成为您的目标,即他们要么不必编写任何代码,要么只需不到 5 分钟即可编写代码。
对于大多数 API,我更喜欢简单的基于令牌的身份验证,其中令牌是分配给用户的随机哈希,如果它被盗,他们可以随时重置它。 允许通过 POST 或 HTTP 标头传入令牌。 例如,用户可以(并且应该)将 SHA-1 令牌作为 POST 变量发送,或者作为“授权:da39a3ee5e6b4b0d3255bfef95601890afd80709”等格式的标头发送。
此外,请选择安全令牌,而不是简短的数字标识符。 不可逆的东西是最好的。 例如,在用户创建期间生成一个 SHA 令牌并将其存储在数据库中是相对简单的。 然后,您可以简单地在数据库中查询与该令牌匹配的任何用户。 您还可以使用唯一标识符和盐值生成令牌,例如SHA(User.ID + "abcd123")
,然后查询匹配的任何用户; 例如, where TokenFromPost = SHA(User.ID + "abcd123")
。
另一个非常好的选择是 OAuth 2 + SSL。 无论如何,您都应该使用 SSL,但 OAuth 2 在服务器端实现起来相当简单,并且库可用于许多常见的编程语言。
如果您创建的 API 应该可以通过 JavaScript 在公共网站上访问,您还需要确保为令牌验证每个帐户的 URL 列表。 这样,没有人可以检查对您的 API 的调用,从您的用户那里窃取令牌,然后自己使用它。
以下是其他一些需要牢记的重要事项:
白名单功能。 API 通常允许您对数据执行基本的创建、读取、更新和删除操作。 但是您不想对每个实体都允许这些操作,因此请确保每个实体都有一个允许操作的白名单。 例如,确保只有授权用户才能运行
/user/delete/<id>
之类的命令。 同样,在用户请求中发送的所有有用标头也需要根据白名单进行验证。 如果您允许 Content-type 标头,请验证用户发送的内容是否与支持的内容类型的 whilelist 相匹配。 如果不是,则发回一条错误消息,例如 406 Not Acceptable 响应。 白名单很重要,因为许多 API 是自动生成的,或者使用黑名单代替,这意味着您必须明确说明您不想要什么。 然而,安全的黄金法则是从零开始,只明确允许你想要的。保护自己免受跨站点请求伪造 (CSRF)。 如果您允许会话或 cookie 身份验证,则需要确保您保护自己免受 CSRF 攻击。 开放 Web 应用程序安全项目 (OWASP) 提供了有关如何排除这些漏洞的有用指南。
验证对资源的访问。 在每个请求中,您需要验证用户实际上是否被允许访问他们所引用的特定项目。 因此,如果您有一个端点来查看用户的信用卡详细信息(例如
/account/card/view/152423
),请确保 ID“152423”引用的是用户真正有权访问的资源。验证所有输入。 来自用户的所有输入都需要安全解析,如果您使用 XML 或 JSON 等复杂输入,最好使用知名库。 不要构建自己的解析器,否则您将陷入痛苦的世界。
规则 5:易于采用
这确实是最重要的规则,并且建立在所有其他规则的基础上。 正如我在文档规则中提到的,请与不熟悉您的 API 的人一起尝试。 确保他们至少可以在几分钟内启动并运行您的 API 的基本实现,即使只是按照教程进行操作。 我认为15分钟是一个很好的目标。
以下是一些简化和促进 API 采用的具体建议:
确保人们可以真正使用您的 API,并且每次都能第一次使用。 让新人偶尔尝试实现您的 API,以验证它不会以某种您已经免疫的方式造成混淆。
把事情简单化。 不要做任何花哨的身份验证。 不要做一些疯狂的自定义 URL 方案。 不要重新发明 SOAP、JSON、REST 或任何东西。 尽可能使用已经实现并被广泛接受的所有工具,这样开发人员只需学习你的 API,而不是你的 API + 10 种晦涩难懂的新技术。
提供特定于语言的库以与您的服务交互。 有一些不错的工具可以自动为您生成库,例如 Alpaca 或 Apache Thrift。 目前 Alpaca 支持 Node、PHP、Python 和 Ruby。 Thrift 支持 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、OCaml、Delphi 等。
简化任何必要的注册。 如果您不是在开发开源 API,或者如果有任何类型的注册过程,请确保在注册时,用户会很快被引导到教程。 并使注册过程完全自动化,无需您进行任何人工交互。
提供出色的支持。 采用的一大障碍是缺乏支持。 您将如何处理和响应错误报告? 文件不清晰怎么办? 不成熟的用户? 论坛、错误跟踪器和电子邮件支持是很好的开始,但请确保当有人发布错误时,您确实解决了它。 没有人愿意看到一个鬼城论坛或尚未解决的大量错误列表。
Web API 总结
Web 服务及其 API 比比皆是。 不幸的是,绝大多数都难以使用。 原因包括糟糕的设计、缺乏文档、不稳定、未解决的错误,或者在某些情况下,以上所有。
遵循本文中的指导将有助于确保您的 Web API 干净、有据可查且易于使用。 这样的 API 确实很少见,因此更有可能被广泛采用和使用。