3. RESTful API 兼容性设计
说到 API 兼容性,我最先想到的就是 RESTful 设计。很多团队一上来就想着怎么把接口做“快”,结果越做越乱,到最后改一个字段牵出一堆 bug。其实,RESTful 的兼容性设计没那么玄乎,说白了就三个核心:资源模型稳定、HTTP 方法语义对、状态码别乱用。
3.1 资源模型稳定:少改多增
我个人习惯,设计 RESTful API 时先把资源模型定死。什么是“资源模型”?就是你 URL 里的名词。比如 /users、/orders、/products。一旦上线,这些路径最好不要改。
为什么?因为客户端已经写死了。你改路径,客户端就得跟着改。我在项目中遇到过好几次,某个团队把 /users/info 改成了 /users/profile,结果前端没同步,线上直接挂了半小时。
举个例子:
// 旧版本
GET /users/123
200 OK
{
"id": 123,
"name": "张三",
"email": "zhangsan@example.com"
}
// 新版本(兼容扩展)
GET /users/123
200 OK
{
"id": 123,
"name": "张三",
"email": "zhangsan@example.com",
"phone": "13800138000" // 新增字段,不破坏旧客户端
}
那如果必须改怎么办?
- 加版本号:比如
/v2/users/123,旧版本保留,慢慢迁移。 - 用查询参数控制:
?fields=id,name,phone,让客户端自己选。
/deleteUser、/createOrder,这违反了 RESTful 最基本的资源抽象。你想想看,动词就是动作,动作就容易被替换成别的东西,兼容性会变得非常脆弱。
| 做法 | 推荐度 | 说明 |
|---|---|---|
| URL 只包含名词 | ✅ 推荐 | 如 /users、/orders/123 |
| URL 包含动词 | ✅ 不推荐 | 如 /deleteUser、/getOrder |
| 新增字段 | ✅ 安全 | 客户端忽略即可 |
| 删除字段 | ❌ 危险 | 会直接导致客户端报错 |
3.2 HTTP 方法语义:别把 POST 当 DELETE 使
HTTP 方法有明确定义,GET 表示查,POST 表示创建,PUT 表示全量替换,PATCH 表示部分更新,DELETE 表示删。这些语义不是摆设,是兼容性的基础。
你为什么不能把 DELETE 操作塞到 POST 里?
我记得有位同事,为了省事,把所有操作都用 POST,参数塞在 body 里。结果客户端缓存失效,爬虫误删数据,监控报警乱成一锅粥。你想想看,GET 是可以被缓存的,POST 不行。一旦语义乱了,整个 HTTP 生态的兼容性就全塌了。
| 方法 | 语义 | 幂等 | 缓存 | 兼容性要点 |
|---|---|---|---|---|
| GET | 查询资源 | 是 | 可缓存 | 不改变状态,永远安全 |
| POST | 创建资源 | 否 | 不可缓存 | 每次请求都可能创建新资源 |
| PUT | 全量替换 | 是 | 不可缓存 | 客户端需发送完整资源 |
| PATCH | 部分更新 | 否 | 不可缓存 | 用 JSON Patch 标准格式 |
| DELETE | 删除资源 | 是 | 不可缓存 | 幂等,重复调用不报错 |
另外,POST 有一个天然优势:它可以用于“动作”类操作,比如 POST /orders/123/refund。但这一定要配合资源模型一起用,不能乱来。
GET /deleteUser?id=123,结果搜索引擎爬虫一抓,数据全没了。这种问题排查起来非常痛苦。
3.3 状态码的兼容扩展:别偷懒用 200 糊弄
状态码是 HTTP 协议层面给你的兼容性工具。很多团队偷懒,所有成功都返回 200,所有错误都返回 500。你想想看,客户端怎么区分“找不到资源”和“服务器挂了”?
正确的做法:用标准状态码表达含义,按需扩展子码。
举个例子:
// 坏做法
POST /users
200 OK
{ "error": "用户已存在" }
// 好做法
POST /users
409 Conflict
{ "code": "USER_EXIST", "message": "用户已存在" }
状态码的兼容性,遵循一个原则:范围兼容。2xx 表示成功,4xx 表示客户端错,5xx 表示服务端错。客户端只需要判断范围就行了。
| 范围 | 含义 | 扩展规则 |
|---|---|---|
| 2xx | 成功 | 可新增子码(如 201、202),但不能改成 4xx |
| 3xx | 重定向 | 301/302 可改,但需要新旧 URL 都保留一段时间 |
| 4xx | 客户端错误 | 400、403、404、409 等,新增子码客户端可能不认识,但至少知道是 4xx |
| 5xx | 服务端错误 | 500、502、503,不要用 5xxx 非标准码 |
我个人习惯,把常用的状态码列一个表,每个接口必须明确用哪个:
200:查询成功201:创建成功204:删除成功(无返回体)400:请求参数有误404:资源不存在409:资源冲突(比如重复创建)500:服务器内部错误
429 Too Many Requests,其实是有标准定义的。但如果你非要扩展,建议 4xx + 自定义 body,别改状态码本身。
3.4 本章核心流程图
下面这张图,把上面三个要点串起来了。你看完就知道,RESTful API 兼容性设计,其实就是从 资源模型 出发,选择合适的 HTTP 方法,然后搭配正确的 状态码。