💡 Key Takeaways
- Resource Naming: The Foundation That Everyone Gets Wrong
- HTTP Methods and Status Codes: Speak the Language Correctly
- Versioning: Future-Proofing Without the Pain
- Pagination, Filtering, and Sorting: Handling Large Datasets
三年前,我看到一家初创企业在融资230万美元后破产,因为他们的API设计根本存在问题,每个新功能都需要重写一半的代码库。我是陈莎莎,过去12年里我在三家不同的独角兽初创公司担任首席API架构师,设计处理每月超过470亿次请求的系统。我所学到的是,REST API设计不是关于遵循严格的规则,而是关于做出深思熟虑的选择,这些选择最终会导致技术卓越或技术债务的积累。
💡 关键收获
- 资源命名:每个人都搞错的基础
- HTTP方法和状态码:正确使用语言
- 版本控制:无痛的未来保障
- 分页、过滤和排序:处理大数据集
自从REST成为默认标准以来,形势发生了剧烈变化。到2026年,我们正在处理边缘计算、每秒进行数千次API调用的人工智能应用以及用户希望从全球任何地方获得低于100毫秒的响应时间。老旧的“只需遵循REST原则”建议已经不再有效。您需要一个务实的、经过战斗考验的检查清单,以适应现代现实。
这篇文章就是那个检查清单。我将分享我在为每天处理数百万收入的公司架构API时所使用的确切框架。这些不是理论最佳实践——而是区分可扩展API与在自重下崩溃API的模式。
资源命名:每个人都搞错的基础
让我从看似微不足道但引发下游问题的事情开始:资源命名。在我的职业生涯中,我审查了超过200个API设计,我估计其中60%的设计存在不一致或混淆的资源命名,造成了连锁问题。
核心原则是:您的URLs应当读作描述资源层次结构的句子。对集合使用复数名词,保持嵌套浅(最多2-3层),并保持严格一致。当我加入我目前的公司时,他们的API具有像/getUser、/user-list和/users/fetch这样的端点——它们都在执行相似的操作。我们用三个月的时间来理顺这个混乱。
我推荐的模式:
- 集合:
/api/v1/users、/api/v1/orders、/api/v1/products - 特定资源:
/api/v1/users/12345、/api/v1/orders/ord_abc123 - 子资源:
/api/v1/users/12345/orders、/api/v1/orders/ord_abc123/items - 对资源的操作:
/api/v1/orders/ord_abc123/cancel(POST)、/api/v1/users/12345/verify-email(POST)
注意有什么缺失?URL路径中的动词(除了非CRUD操作)。HTTP方法就是动词。GET /users表示“获取用户”。POST /users表示“创建用户”。DELETE /users/123表示“删除用户123”。这不仅仅是美观——这使得您的API可预测,并降低了开发者的认知负担。
对于非CRUD操作,我采用务实的方法。是的,纯粹主义者会说一切都应该映射到CRUD,但在现实世界中,您有像“取消订单”、“验证电子邮件”或“计算运费”这样的操作。我将这些表示为对操作端点的POST请求:POST /orders/123/cancel。关键是一致性——记录您的模式,并严格遵循。
还有一个重要细节:使用kebab-case表示多词资源(/shipping-addresses,而不是/shippingAddresses或/shipping_addresses)。在许多上下文中,URLs是不区分大小写的,而kebab-case是最通用的可读格式。我见过因大小写敏感问题导致的生产事件,使用这个简单的约定可以避免。
HTTP方法和状态码:正确使用语言
如果资源命名是您API的词汇,HTTP方法和状态码就是它的语法。就像在人类语言中,使用错误的语法使您难以理解——即使人们最终能够搞清楚您的意思。
我反复看到两种常见的反模式。首先,使用POST处理所有操作的API,因为“它能工作”。其次,所有响应都返回200 OK的API,甚至错误响应,其实际状态埋在响应体中。这两种模式都会使API变得更难缓存、更难调试、更难与标准工具集成。
以下是我根据实际使用情况的逐方法分析:
GET: 检索资源。必须是幂等的和安全的(没有副作用)。绝不要使用GET来进行修改状态的操作——我不在乎它是否“只是更新一个最后访问的时间戳”。这就是中间件的目的。GET请求应该是可缓存的,将状态更改混入其中破坏了缓存假设。在我参与的一个系统中,我们通过正确实现GET的幂等性并添加HTTP缓存头,数据库负载减少了73%。
POST: 创建新资源或触发操作。POST是您处理非幂等操作的工作马。创建资源时,返回201 Created,并在Location头中指向新资源。当触发操作时,返回200 OK或202 Accepted(用于异步操作),并在响应体中描述结果。
PUT: 替换整个资源。这是许多开发者感到困惑的地方。PUT应该用提供的表示替换完整资源。如果您发送一个PUT请求仅包含一些字段,那这些字段就是资源后续应有的唯一字段(其他字段应设置为默认值或null)。在实践中,我很少使用PUT——通常只对客户真正管理完整状态的资源使用。
PATCH: 部分更新资源。这就是大多数开发者在认为自己想要PUT时真正想要的。PATCH让您可以仅发送要更改的字段。我通常使用JSON Patch(RFC 6902)或JSON Merge Patch(RFC 7396)作为请求格式。在我目前的公司,我们94%的更新操作使用PATCH,而不是PUT。
DELETE: 删除资源。成功时返回204 No Content(不需要响应体),或者在返回删除信息时返回200 OK。使DELETE具有幂等性——多次调用其效果应与一次调用相同。即使资源已经被删除,也返回204。
对于状态码,我使用这个覆盖99%场景的实用子集:
- 200 OK: 成功的GET、PUT、PATCH或返回数据的POST
- 201 Created: 成功的POST创建资源
- 202 Accepted: 请求已接受进行异步处理
- 204 No Content: 成功的DELETE或没有响应体的更新
- 400 Bad Request: 客户端发送了无效数据(附带详细错误信息)
- 401 Unauthorized: 需要或失败的身份验证
- 403 Forbidden: 已身份验证但未授权访问该资源
- 404 Not Found: 资源不存在
- 409 Conflict: 请求与当前状态冲突(例如,重复的电子邮件)
- 422 Unprocessable Entity: 验证失败(对于验证错误,我更喜欢这个而不是400)
- 429 Too Many Requests: 超过速率限制
- 500 Internal Server Error: 我们这边出现了问题
- 503 Service Unavailable: 临时故障或维护
关键见解:状态码用于HTTP层面的语义,响应体用于应用层面的细节。400响应应该总是意味着“您发送了错误的数据”,而响应体准确解释了哪些字段出现了问题。
版本控制:无痛的未来保障
我经历过三次主要的API版本迁移,每一次都让我痛苦地学会了哪些事情是不能做的。最糟糕的是一次v1到v2的迁移,花费了18个月,并要求协调对47个客户端应用程序的更新。在这个过程中,我们失去了两个主要客户,因为他们的集成团队无法跟上变化。
| 方法 | URL模式 | 可扩展性 | 可维护性 |
|---|---|---|---|
| RESTful(推荐) | /users/{id}/orders | 优秀 - 清晰的层次结构,可缓存 | 高 - 可预测的模式 |
| RPC风格(反模式) | /getUser, /fetchOrders | 差 - 不可缓存,基于动词 | 低 - 不一致的命名 |