重构与API设计:重构RESTful API的接口与参数

API重构这事儿,说实话比后端逻辑重构要敏感得多。你改个内部函数,大不了跑一遍单元测试。但API一旦上线,客户端、第三方、甚至你同事写的脚本都可能依赖它。改不好,就是线上事故。

我个人习惯是:先保证兼容,再谈优化。今天咱们就聊聊,怎么在不动声色的情况下,把那些丑陋的接口和参数给收拾干净。

一、接口路径的“坏味道”与重构

先看一个我接手过的真实案例。某个老系统的API长这样:

GET /getUserInfo?userId=123
POST /updateUserData
DELETE /removeUserRecord?userId=123

你发现问题了吗?动词全写在路径里了。RESTful风格讲究的是用HTTP动词表达操作,路径只应该表示资源。我见过不少团队,一开始图省事,后面越堆越乱。

重构后的版本:

GET    /users/123
PUT    /users/123
DELETE /users/123

嗯,这里要注意:路径重构不能直接删旧接口。你得先加新接口,等所有调用方迁移完毕,再废弃旧的。我一般会留一个过渡期,至少两个版本迭代。

核心原则:资源路径用名词复数,HTTP动词表达操作。不要出现 /getUser、/createOrder 这种“动词+名词”的混搭。

二、参数设计的“坑”与填坑

参数设计是API重构里最容易出幺蛾子的地方。我曾经因为一个参数名改了个大小写,导致前端三天没睡好觉——当然,这是夸张说法,但确实很麻烦。

常见的参数问题:

  • 参数名不统一:有的接口用 userId,有的用 user_id,有的用 uid
  • 参数类型模糊:同一个字段,有时传字符串,有时传数字
  • 可选参数过多:一个接口十几个参数,大部分都是可选的

举个例子,一个订单查询接口:

// 重构前
GET /orders?status=1&page=1&size=20&sort=time&order=desc&startDate=2024-01-01&endDate=2024-12-31&keyword=手机

这接口看着就头疼。参数一多,调用方容易漏传、错传。我的做法是:把强相关的参数打包成子资源或查询对象

// 重构后
GET /orders?filter=status:1,dateRange:2024-01-01~2024-12-31&page=1&size=20&sort=-time

你看,我把状态和日期范围合并成了一个 filter 参数,排序也简化了(用负号表示降序)。当然,这种设计需要前后端约定好解析规则。

小技巧:参数名尽量用驼峰(camelCase)或下划线(snake_case),但一个项目里只能选一种。我个人偏好下划线,因为URL里不区分大小写,下划线更清晰。

三、版本管理的“温柔”策略

API版本管理,说白了就是给重构留后路。我见过两种主流做法:

方式 示例 优点 缺点
URL路径版本 /v1/users/123 直观,容易路由 版本号污染URL
请求头版本 Accept: application/vnd.myapp.v1+json URL干净,语义化 调试不方便

我个人习惯用URL路径版本。为什么?因为简单。你想想看,前端同学调试的时候,直接在浏览器地址栏改个版本号就能切版本,多方便。用请求头的话,还得装插件或者写脚本。

但要注意:版本号不要轻易升。每次升版本,意味着你要同时维护两套代码。我见过一个项目,v1到v5共存,光维护就累死人。我的建议是:最多同时维护两个大版本,旧版本超过半年就强制下线。

四、响应结构的“统一”之道

响应结构不统一,是API重构里最让人抓狂的问题之一。有的接口返回:

{ "code": 0, "data": { ... }, "msg": "success" }

有的接口返回:

{ "status": "ok", "result": { ... } }

还有的直接返回一个数组:

[ { "id": 1, "name": "张三" }, ... ]

你让前端怎么统一处理?每次都得写兼容代码。重构的目标就是:所有接口返回统一的结构

{
  "code": 0,
  "message": "success",
  "data": {
    // 具体业务数据
  },
  "meta": {
    "page": 1,
    "size": 20,
    "total": 100
  }
}

code 表示业务状态码,0 是成功,非0是各种错误。message 给开发者看,data 放业务数据,meta 放分页等元信息。这样前端只需要写一个统一的拦截器,所有接口都能用。

避坑指南:我曾经把错误信息直接放在 message 字段里返回给前端,结果前端直接展示给用户看了。后来我加了一个 userMessage 字段,专门放用户能看懂的错误提示。技术细节和用户体验,一定要分开。

五、知识体系总览

下面这张图,是我对API重构核心思路的总结。你可以把它当作一个检查清单:

API重构核心 路径设计 参数设计 版本管理 响应结构 兼容策略 资源名词 + HTTP动词 统一命名 + 精简参数 最多两个大版本 统一code/data/meta

这张图把API重构拆成了五个维度。路径设计是骨架,参数设计是血肉,版本管理是保险,响应结构是门面,兼容策略是底线。缺一个,重构就容易翻车。

六、实战中的“灰度”策略

最后聊点实际的。API重构不像内部代码重构,你不能“改完就上线”。我通常的做法是:

  1. 加新接口,不改旧接口:新功能用新接口,旧接口保持不动
  2. 标记废弃:在旧接口的响应里加一个 Deprecated 头,或者返回一个 warning 字段
  3. 监控调用量:等旧接口的调用量降到零,再考虑下线
  4. 发公告:提前两个版本通知调用方,给足迁移时间

我记得有一次重构用户接口,旧接口有十几个调用方。我花了整整一个迭代周期,挨个通知、协助迁移。最后旧接口下线那天,我盯着监控看了半小时,确认没有报错才松了口气。

API重构,技术难度其实不大。难的是沟通、协调、和那颗对线上环境保持敬畏的心。

公众号:蓝海资料掘金营,微信deep3321