良好的 API 设计是一个经常被提及的话题,特别是对于那些试图完善其 API 策略的团队来说。一个设计良好的 API 的好处包括:改进开发者体验、更快速地编写文档以及更高效地推广你的 API。但是,到底什么才构成了良好 API 设计呢?在这篇博客文章中,我将详细介绍几个为 RESTful APIs 设计最佳实践。
一般来说,一个有效的 API 设计将具有以下特点:
易于阅读和使用: 设计良好的 API 将很容易使用,并且开发人员可以快速记住其资源和相关操作 。
难以误用: 实现和集成具有良好设计的 API 是一个简单直接的过程,并且减少编写错误代码。它提供了信息反馈,并且不会对 API 最终用户强制执行严格指南。
完整而简洁: 完整的 API 将使开发人员能够针对您公开数据创建全面应用程序。通常情况下,完成需要时间,大多数 API 设计师和开发人员在现有 API 上逐步构建。这是每个拥有 API 工程师或公司必须努力追求的理想状态。
为了说明下面列出的概念,我将以一个照片分享应用程序为例。该应用程序允许用户上传照片,并使用拍摄这些照片的位置和描述与之相关联情感的标签来对它们进行特征化。
资源是 REST 概念的基础。一个资源是一个重要到足以被引用本身的对象。一个资源有数据,与其他资源的关系,以及操作它来允许访问和操作相关信息的方法。一组资源称为集合,集合和资源内容取决于您的组织和消费者需求。例如,如果您认为市场将受益于获取有关产品用户群体的基本信息,则可以将其公开为集合或资源。统一资源定位符(URL)标识了一个资源在线位置。这个 URL 指向 API 所在位置中资源存在处 。基础 URL 是此位置的一致部分,在照片共享应用程序中,我们可以通过适当的 URL 访问通过集合和资料库使用该应用程序用户数据。
基本的 URL 应该整洁、优雅和简单,这样开发人员就可以在他们的 Web 应用程序中轻松使用它们。一个长而难以阅读的基本 URL 不仅看起来不好,而且在尝试重新编码时也容易出错。对于一致性,在所有资源和集合上都保持相同数量是很好的做法。将这些名词自我解释有助于开发人员了解从 URL 描述的资源类型,最终可以使他们在使用 API 时变得更加独立自主。
回到照片共享应用程序,假设它有一个公共 API,并具有 /users 和 /photos 作为集合,请注意它们都是复数名词,它们也是自我说明性质并且我们可以推断出 /users 和 /photos 分别提供关于产品注册用户群体和分享照片信息。
用 HTTP 方法描述资源功能
所有资源都有一组可以对其进行操作的方法,以处理 API 公开的数据。RESTful API 主要由具有明确定义和独特操作的 HTTP 方法组成,这些方法定义了任何资源或集合的 CRUD 操作。以下是常用的 HTTP 方法列表,它们为 RESTful API 中任何资源或集合定义了 CRUD 操作:
方法 | 描述 |
---|---|
GET | 用于检索资源的表示形式 |
POST | 用于创建新资源和子资源 |
PUT | 用于更新现有资源 |
PATCH | 用于更新现有资源 |
DELETE | 用于删除现有资源 |
在 URL中避免使用动词也是一个好主意。操作 GET、PUT、POST 和 DELETE 已经用于操作由 URL 描述的资源,因此在 URL中使用动词会使您的资源处理变得混乱。在照片分享应用程序中,以 /users 和 /photos为 终点,API 的最终用户可以轻松直观地使用上述 RESTful CRUD 操作来处理它们。
向开发人员提供良好的反馈,告诉他们如何更好地使用您的产品,这将有助于提高采用率和留存率。每个客户端请求和服务器响应都是一条消息,在理想的 RESTful 生态系统中,这些消息必须是自描述的。良好的反馈包括对正确实现进行积极验证,并在不正确实现时提供信息性错误,以帮助用户调试并纠正其使用产品的方式。
对于 API 而言,错误是提供 API 上下文信息的绝佳方式。将您的错误与标准 HTTP 代码保持一致,不正确、客户端调用应该与 400 类型错误相关联。如果存在任何服务器端错误,则必须与适当的 500 响应相关联。针对资源使用成功方法应返回 200 类型响应码。还有很多其他响应代码,请参阅此 REST API 教程获取更多信息。
总体而言,在使用您的 API 时可能会出现三种可能结果:
在使用 API 过程中,如果最终用户遇到问题,为其提供支持将有助于改善开发者体验并防止 API 滥用。对这些错误响应进行清晰简洁的描述,并提供足够的信息以便最终用户开始修复原因。如果您认为需要更多信息,请提供文档。
一个设计良好的 API 还应该有一种成功调用URL时,期望响应类型的示例,这个示例响应可以被简单、明了和快速理解。一个很好的法则是五秒内帮助开发人员准确地理解成功响应会给他们什么。回到我们的照片分享应用程序,我们定义了 /users和/photos URL/users 集合将以数组形式提供已注册该应用程序的所有用户的用户名和加入日期。
responses: 200: description: Successfully returned information about users schema: type: array items: type: object properties: username: type: "string" example: "kesh92" created_time: type: "dateTime" example: "2020-01-12T05:23:19+0000"
请注意每个响应项中描述的数据类型和示例,最终用户可以从成功的 GET 调用中获得这些信息。最终用户将在 JSON 格式下收到以下成功响应:
{ “data”:[ { “Username”:“example_user1”, “created_time":“2013-12-23T05:51:14+0000 ” }, { “username”:“example_user2”, “created_time":“2015-3-19T17:51:15+0000 ” } …. ] }
如果最终用户使用 GET 方法成功调用端点,则用户应该获得上述数据以及 200 响应代码,以验证正确的使用。同样,不正确的调用应产生适当的 400 或 500 响应代码,并提供相关信息来帮助用户更好地操作集合。
公开的数据可以由许多属性来描述,这些属性对于使用您的 API 的最终消费者非常有益。这些属性描述了基本资源并隔离了可以使用适当方法操纵的特定信息资产。API 应该努力实现完整性,并提供所有必需的信息、数据和资源,以帮助开发人员无缝集成它们。但是完成意味着考虑到 API 的常见用例。可能会有许多此类关系和属性,为每个关系定义资源不是一个好习惯。还应考虑资源公开的数据量。如果您试图暴露大量数据,则服务器可能会出现负面影响,尤其是在负载和性能方面。上述情况和关系是设计 API 时重要考虑因素,并可使用适当参数进行处理。您可以将属性扫描并将响应限制在查询参数中“?”后面,或者使用路径参数隔离客户端正在处理的数据组件中特定部分 。让我们以我们照片分享应用程序为例子吧!对于开发人员而言,在特定位置和具体标签下获取所有共享照片信息可能很有用处;同时你也想通过每次调用 API 返回 10个结果来限制服务器负载压力. 如果最终用户想要查找带有 #winter 标签波士顿市内所有照片,则调用如下:
GET /photos?location=boston&hashtag=winter&limit=10
请注意,现在复杂性已经被简化为与查询参数的简单关联。如果您想根据客户端请求提供有关特定用户的信息,则调用将是:
GET /users/kesh92
kesh92 是用户集合中特定用户的用户名,将返回 kesh92 的位置和加入日期。这些只是您设计参数以实现 API 完成并帮助最终开发人员直观使用 API 的一些方法。
如果您对特定资源或集合的功能有所顾虑,则将其保留到下一个迭代中。开发和维护 API 是一个持续过程,并等待来自正确用户的反馈可以在构建强大的 API 方面产生长远作用,使用户能够以创造性方式集成和开发应用程序。
没有一种适用于所有组织的 API 设计方法,上述只是推荐方法。为什么 API 设计如此重要的一个原因是,它可以帮助最终消费者使用您的 API,他们的需求应该成为设计和构建出色 API 的指路明灯。
Eolink 翻译,原文地址:https://swagger.io/resources/articles/best-practices-in-api-design/