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,让客户端自己选。
注意:千万不要在 URL 里塞动词。比如 /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 删除资源 是 不可缓存 幂等,重复调用不报错
小技巧:如果你用 PUT 做更新,一定要保证客户端发送的是完整资源。少一个字段,服务端就可能覆盖成空值。我倾向于用 PATCH,更灵活,也更容易做向后兼容。

另外,POST 有一个天然优势:它可以用于“动作”类操作,比如 POST /orders/123/refund。但这一定要配合资源模型一起用,不能乱来。

坑:GET 请求永远不能有副作用。我曾经见过有人用 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 方法,然后搭配正确的 状态码。

RESTful API 兼容性设计核心流程 资源模型设计 是否可扩展? 直接新增字段 加版本号或新接口 HTTP 方法交付 状态码兼容扩展
总结一下:资源模型是地基,别动。HTTP 方法是工具,别误用。状态码是信号,别含糊。这三块稳住了,你的 API 就具备基本的兼容性了。