API 设计实践
给系统设计一套 API 不是一件简单的事情。 原则是否能被所有人真正理解, 是否能准确通过 API 来表达对资源的正确处理都是学习的目的。
设计 API 的简单原则
- 一致性
- 显而易见性
- 可组合性
- 自解释
- 可进化性
一致性
在 API 中使用一致的术语。 例如, 统一使用驼峰, 或者统一使用蛇形命名
显而易见性
- 为空值仍然返回 JSON 的 key,而不是从 payload 中直接移除 key
- 对于创建(create)和删除(delete)操作, 在请求结果仍然显示相关的资源信息,以便开发人员可以在创建和删除后仍能看到完整的资源
- 在分页的情况下,发送
totalCount、nextPage、previousPage
可组合性
- API 不应绑定到任何特定的 workflow
- 故事是由 API 驱动, 而非由 workflow 驱动
- 将 workflow 保留在用户旅程中而不是 API 中
- API 不应该是数据库表结构的特定映射
自解释
- API 应该可以在没有编写 API 的团队干预的情况下使用
- 规范和文档是 first-class 的实体,任何想要使用 API 的人都应该可以访问。 它们应该在发布之前进行测试(或至少手动验证)
可进化性
- 规格变更应受监管
- 应该能够添加新字段(关闭严格反序列化)
- 尽可能设计通用的 URI 和响应格式(例如,代替
homePhoneNumber,使用phoneNumber: { type=”home” })
资源的命名
集合:使用复数名词, 不包含唯一标识符
- GET: 用于过滤、搜索、排序和分页。 对多个结果使用查询参数
例如: 查询字符串 - 用于非资源属性 -
?sort=name?page=1?format=json - POST: 用于创建集合的新成员
- PUT 和 DELETE: 很少用于处理集合。 通常用于更新集合而非更新集合中的个体
单一资源:使用标识符(* 不要使用主键/递增数值)
- GET:返回指定的资源
- PUT: 更新指定的资源。 并发控制以避免同时更新
- PATCH: 使用更改更新资源
- DELETE: 删除指定的资源
Notes: 使用唯一标识符 - 不必是主键。 保证它们独特且可读即可
嵌套资源
- 在父单一资源下可单独寻址的资源。 例如:用户的订单 -
users/1/orders - 尽量避免嵌套超过 2 层(可组合性)
- 嵌套资源既作为嵌套集合存在,也作为嵌套单一资源存在
具化的资源
具体化是将抽象概念具体化的行为(关注意图,而不是实体), 这有时在 RESTful 建模中很有用。
与简单的 CRUD 操作相反,具体化的资源通常代表更改的意图。 因此,他们倾向于避免使用 PUT 来支持对某些用户操作进行建模的不可变资源。
非资源的 endpoints
可能有一些考虑迫使我们设计一个非资源性的 endpoint。 例如,您可能决定支持将信用卡搜索作为 POST 而不是 GET, 因为我们希望将信用卡号从 URL 中移除。 在这种情况下,我们显然是有意打破了 REST 的统一接口架构约束。 为了清楚地表明这是有意的,我们应该在路径上放置一个动词,将其标记为 RPC 而不是 RESTful
| Name | Example |
|---|---|
| 集合 | /orders |
| 单一资源 | /orders/1 |
| 嵌套资源 | /orders/1/product |
| 具化资源 | /registrationRequest |
| 非资源型 | /creditCards/search |
其它
- 避免使用动词。 REST 已经为您提供了动词。
- GET、 PUT 和 DELETE 应该是幂等的。 任何与输入请求有关的具有不确定结果的操作都应建模为 POST。 例如; PUT 应该总是返回,好像有什么改变了。 不应返回“未检测到更改”
- 此外,GET 被定义为“安全”的,这意味着它不应影响服务器的状态。 GET 请求应该是可缓存的。
- 如果不保证幂等性的 POST 操作需要幂等性,客户端可以传递带有客户端生成的 guid 的 Idempotency-Key 标头。 服务器可以确保只处理一条消息。
- 不要让你的 API 变得啰嗦(细粒度与粗粒度)。 例如:单独的 POST 以添加喜欢、评论、标签等
API 版本化
-
/api/v1/pros: 在 api URL 中清晰地进行版本控制 cons: 每次版本更改时,client 都需要更改 URL -
?v=1.0pros: 可选择是否使用包含版本控制的 API(可以使用默认版本) cons: client 太容易错过需要该版本 -
In headers:
X-Version: 1.0pros: 将版本控制与 API 的其余部分分开 cons: 维护 headers -
Accept Header:
Accept: version=1.0pros: 无需创建自己的自定义标题 cons: 比query更不容易被发现 -
使用
Content Type进行版本控制:content-type: application/<custom content type>pros: version payload 和 API 本身一样具有意义 cons: 需要更多的开发人员成熟度
虽然 API 设计的最终目标是无版本化,但在日常开发中仍然要有计划的使用版本:
- 将版本放在 URL 中: 允许您在浏览器中打开它, 通过电子邮件发送它, 添加书签。 在日志中也能清晰可见
- 仅使用主要版本: API 使用者应该只关心重大更改。
- 使用数字类型的版本号:不是像日期这样对消费者来说不重要的其他信息
- 版本是必选项:否则为了向后兼容还必须支持未版本化的 URL。
API 认证
-
Cookies
- 简单, 不是很安全
-
Basic Auth
- 易于实施
- 除非使用 SSL,否则不安全
- 在每个请求中发送凭据
-
Token Based Auth
- 简单安全
- 行业标准令牌 - 5 到 20 分钟后到期
- 通常使用 JWT(JSON Web Tokens)
-
OAuth
- 第三方认证
- 使用请求令牌和访问令牌
API 网关
- 尽量减少内置的冗余功能, 例如 API 生命周期管理
- 当有跨越团队边界时, 通过网关进行 service-to-service call
| 能力 | 一般倾向于我们是否应该使用 api 网关执行此操作 ? |
|---|---|
| 截流 & 定量 | 是 |
| 跟踪记录 API 活跃度 | 是 |
| API 生命周期管理 | 是 |
| 认证 | 也许 |
| 缓存 | 也许 |
| 公布 API 文档 | 也许 |
| 负载均衡 | 也许 |
| 服务发现 | 也许 |
| service mocks | 也许 |
| API 编写 | 否 |
| 数据转化 | 否 |
| 细粒度权限 | 否 |
API 管理
-
最佳实践
- 规范驱动开发允许消费者在完全开发之前开始试验 API
- 通过测试平台使用 API 规范,例如提供 API 调用存根实现的托管 OpenAPI 文件
-
如何提供 API 规范
- 单独的规范文件
- 内联规范
-
工具
- OpenAPI
- API 蓝图
- RAML
API 文档
- 根据规范自动生成文档
- 包括示例请求/响应
- 支持多个版本
- 支持自定义
- 支持 stub 响应
- 安全访问(文档和 API)
- 使文档保持最新(将文档作为部署管道的一部分发布 - Dredd 工具)
- 在文档旁边包含 SLO(服务水平目标)
工具
- Gelato (closely tied to Kong)
- Readme.io -Generates API documentation by parsing a hosted Swagger file
- Stoplight - handles integration with Auth0
- Swagger Hub
- Swagger-UI
- Apiary - Provides easy configuration of server mocks to prototype APIs
- API portal (Mulesoft)
- SmartDocs
- Swagger Editor
- API Studio
API Mocks
- 规范驱动开发
- 先暴露 mock endpoints
- 消费者驱动契约测试
- 使用 mountebank 等工具针对不提供 mock endpoints 的 API 进行测试
- 工具
- mountebank
- sandbox
其它
- 分页
- 在 API URL 中使用 page number 和 page limit
- 使用
E-Tags进行缓存- 并发
- 使用标头 If-Match 和 If-No-Match
- 在请求中发送 If-Match 中的 E-Tag。 如果服务器上的标签匹配,则将进行更改。 否则,将返回错误代码
- Functional API - (非 RESTful 的 Operational API)
- 例如:重启机器,重新计算总价,计算税务
- 不是构建 RPC API 的理由
- 文档化
- 可以使用其他 REST 动词,例如 OPTIONS、HEAD、LINK
- 异步 API
- 非 REST 解决方案很有用 - Comet、gRPC、SignalR、Firebase、Socket.IO 等。
- ← Previous
在团队中引入新技术的3个原则 - Next →
我的 2021 年度总结