API 设计评审
概述
从客户端视角评判 API:可预测的名称、诚实的状态码、统一的错误格式、安全的重试机制,以及无意外破坏性变更。每个发现都指向具体的路由,并展示修正后的设计。
方法
- 清单。 将每个端点列成
方法 /路径 → 状态码 → 请求/响应格式。读取路由、控制器、OpenAPI 文件和 DTO。 - 对照规则检查每个端点。
- 将 API 作为一个整体检查: 端点间的一致性比任何单一选择都重要。
- 变更: 如果审查的是差异,比较新旧契约,并标记任何会破坏现有客户端的内容。
- 使用以下契约报告。
规则
| 领域 | 规则 |
|---|---|
| 资源 | 复数名词,无动词:POST /orders,而不是 /createOrder。仅在真正拥有关系时嵌套:/users/{id}/orders。 |
| 方法 | GET 读取且安全。POST 创建。PUT 替换。PATCH 部分更新。DELETE 删除。千万不要在 GET 上改变状态。 |
| 状态码 | 200 OK,201 Created(+ Location),204 No Content,400 无效输入,401 未认证,403 未授权,404 未找到,409 冲突,422 验证失败,429 限流,5xx 仅用于服务器故障。绝不能用 200 返回 {"error": ...}。 |
| 错误 | 统一的格式,例如 RFC 9457:{ "type", "title", "status", "detail", "errors": [{ "field", "message" }] }。响应中不要包含堆栈跟踪或 SQL。 |
| 命名 | JSON 字段使用统一大小写(camelCase 或 snake_case),ID 一致,时间戳使用 ISO 8601 UTC,金额采用整数最小单位或带币种的十进制字符串。 |
| 集合 | 对每个列表进行分页(对于大型或变化中的数据,推荐使用游标),限制页面大小,返回下一个游标或链接。通过查询参数实现过滤和排序。 |
| 幂等性 | 重试创建支付、订单或邮件的 POST 请求应接受 Idempotency-Key。PUT 和 DELETE 天然具有幂等性。 |
| 版本控制 | 明确的策略(/v1 路径或请求头)。破坏性变更应发布新版本。 |
| 破坏性变更 | 删除或重命名字段、更改类型、将可选输入改为必需、更改状态码或错误格式、收紧验证规则。 |
| 安全性 | 每个非公开路由都需要认证、对象级别所有权检查、认证和昂贵路由需要限流、URL 中不要包含敏感数据。 |
| 性能 | 无边界响应,支持 fields 或稀疏字段集以应对复杂对象,可缓存的 GET 请求应包含 ETag 或 Cache-Control。 |
输出契约
以 N 个端点已审查,M 个发现(X 个破坏性变更)。 开头。然后,按影响程度从高到低排列:
### [破坏性 | 高 | 中 | 低] 标题
- 端点:方法 /路径 (文件:行号)
- 问题:客户端会遇到什么
- 修复:修正后的路由、状态码或 JSON 格式
最后用一个简短的一致性总结(大小写、错误格式、分页、版本控制:是否一致)结束,并且如果是从头开始设计,提供建议的端点表。
常见错误
- 逐个审查端点,却遗漏了两个端点之间在大写或错误格式上不一致的问题。
- 当向后兼容的添加就能解决问题时,却推荐重新设计。
- 因为服务器代码仍能工作就认为变更是安全的:检查现有客户端发送了什么以及期望什么。
- 未经慎重考虑就使用 500 表示验证错误,或使用 404 表示权限错误。