第二章 RESTful API 设计原则
这一章,我们来聊聊 RESTful API 设计里那些最核心的东西。很多人觉得 REST 就是“URL 漂亮点,用对 GET/POST”,其实不然。
我个人习惯,在设计任何一个 API 之前,先把这三个问题想清楚:资源是什么?怎么命名?动作怎么映射?这三个问题搞定了,剩下的都是细节。
2.1 REST 架构风格的核心概念
REST 不是一个协议,也不是一个标准。它是一种架构风格。说白了,就是一套约束条件,教你如何构建一个分布式、可伸缩的 Web 服务。
这里有几个核心概念,我挑最重要的讲:
- 资源 (Resource):一切皆资源。用户、订单、令牌()、卡片……都是资源。每个资源都有一个唯一的标识符,就是 URI。
- 表现层 (Representation):资源在客户端和服务端之间传输的形态。JSON、XML、甚至纯文本都可以。我们做令牌化服务,绝大多数情况用 JSON。
- 无状态 (Stateless):每个请求都是独立的,服务端不保存客户端上下文。我记得刚入行时,总想把用户登录状态存在 session 里,这在 REST 里是大忌。
- 统一接口 (Uniform Interface):标准的 HTTP 方法,标准的资源操作。这就是 REST 的“约定优于配置”。
核心要点:REST 的核心是“资源”和“状态转移”。客户端通过操作资源的表现层,来改变服务端资源的状态。你想想看,是不是这个理?
2.2 资源命名规范
给资源起名字,是件挺讲究的事。我见过太多乱七八糟的命名,比如 /getUserInfo、/queryOrderListById,一看就知道是 RPC 风格,不是 REST。
我建议,遵循以下几条原则:
- 使用名词,而不是动词。用
/orders,不要用/getOrders。动词交给 HTTP 方法去表达。 - 使用复数形式。集合用复数,比如
/users、/tokens。单个资源用/users/{userId}。 - 用短横线 (-) 分隔,不用下划线 (_) 或驼峰。比如
/payment-methods比/payment_methods或/paymentMethods更易读。 - 用斜杠 (/) 表示层级关系。比如
/users/{userId}/cards,表示某个用户下的所有卡片。 - 用查询参数(Query String)做筛选、排序、分页。比如
/tokens?status=active&page=1&size=20。
| 正确示例 ✅ | 错误示例 ❌ | 原因 |
|---|---|---|
/tokens/{tokenId} |
/getTokenById |
动词不能出现在路径中 |
/cards |
/card |
集合资源用复数 |
/merchants/{id}/transactions |
/merchant-transactions |
用斜杠表示层级,更直观 |
/users?role=admin |
/getUsers?role=admin |
查询参数不是动词的替代品 |
避坑指南:我曾经在一个项目中,看到团队把所有 API 都设计成了 /api/v1/doSomething.do 的形式,导致后来无法快速定位资源。从那时起,我就定了个规矩:资源命名,必须一眼能看出它代表什么实体。
2.3 HTTP 方法使用
HTTP 方法,才是 REST 里真正的“动词”。每个方法有它自己的语义和幂等性要求。我整理了一个速查表:
| HTTP 方法 | 语义 | 幂等性 | 令牌化服务场景 |
|---|---|---|---|
GET |
获取资源 | 是 | 查询令牌详情、查询卡片列表 |
POST |
创建资源(或触发非幂等操作) | 否 | 创建新令牌、发起扣款请求 |
PUT |
全量替换资源 | 是 | 更新令牌的完整配置 |
PATCH |
局部更新资源 | 否(需要谨慎设计) | 更新令牌的状态(如激活/冻结) |
DELETE |
删除资源 | 是 | 作废一个令牌 |
个人经验:在设计 POST 创建资源时,我习惯让客户端传入一个 idempotency-key(幂等键)。这样即使客户端重试,也不会重复创建令牌。这是我们后端工程师的“保命符”。
那为什么 PUT 和 PATCH 要区分?
因为语义不同。PUT 是全量覆盖,你传什么,服务端就存什么,没传的字段会被清空。PATCH 是增量更新,只修改你传的字段。嗯,这里要注意:很多团队把 POST 当作万能方法,这是不对的。该用 GET 查,就用 GET;该用 DELETE 删,就别用 POST。
2.4 知识脉络图
下面这张图,把本章的三个核心原则串起来了。从资源抽象开始,到命名规范,再到 HTTP 方法的正确使用,最后落到你的代码里。
这套设计原则,说起来简单,写起来全是细节。我自己在深入重构一个老旧支付系统时,就踩过不少坑。但只要你把“资源”这个模型想清楚,命名和方法自然就顺了。