4. 接口设计规范
接口设计这东西,说白了就是给系统之间的对话定规矩。规矩定得好,对接的时候谁都不难受;规矩定得差,嗯,后面全是坑。我在好几个项目里都见过因为接口规范没统一,联调阶段反复返工,最后直接延期上线的情况。
这一章咱们就聊聊接口命名、参数、返回值这些核心规范。我还会对比一下RESTful和RPC两种主流风格,说说各自什么时候用、怎么用。你想想看,SDK本质上就是一套接口集,规范立住了,SDK的质量也就立住了。
4.1 命名规范——一眼看懂接口在做什么
接口命名是第一印象。好的命名,看方法名就知道是查还是改、资源是什么;烂的命名,看完参数还要猜半天。
- RESTful 风格:资源名用名词复数,用 HTTP 方法表达操作。
GET /users # 查询用户列表 POST /users # 创建用户 GET /users/{id} # 查询单个用户 PUT /users/{id} # 全量更新用户 DELETE /users/{id} # 删除用户 PATCH /users/{id} # 部分更新用户 - RPC 风格:用动词+名词,直接表达动作。
userService.getUserById userService.createUser userService.updateUser userService.deleteUser
我个人的习惯是:对外公开API优先用RESTful,因为它语义清晰、缓存友好;内部服务间通信用RPC,简单直接、效率高。
命名时还要注意:统一大小写风格(常用小驼峰或蛇形)、避免缩写、版本号放在路径或请求头。
4.2 参数规范——让调用者少翻文档
参数设计做不好,调用方每接一个接口都要去查参数说明。我之前做个支付SDK,有个接口参数列表一长串,大部分还是可选的,结果光联调就花了三天。后来我把必填参数和可选参数分开,把关联参数用对象封装,问题才解决。
4.2.1 必填 vs 可选
必填参数一定要在文档里明确标注,并且如果缺失要返回清晰的错误码。可选参数要有合理默认值。
4.2.2 参数位置
| 风格 | 路径参数 | 查询参数 | 请求体 |
|---|---|---|---|
| RESTful | 资源标识(/users/{id}) | 筛选、分页(?page=1) | 实体数据(JSON) |
| RPC | 不需要 | 不需要 | 方法参数封装在请求体 |
小技巧:分页参数永远统一叫 page、size,排序参数统一叫 sort、order。这样SDK使用者无论对接哪个接口,都不需要重新学习。
4.2.3 参数校验
服务端必须做参数校验。不要信任客户端传来的任何值。我曾经见过一个系统,因为没做参数范围校验,客户端传了个超大数字直接导致数据库OOM——这教训太深刻了。
4.3 返回值规范——成功和失败都要明明白白
返回值设计是最容易被忽视的。很多人只关心成功返回,忘了定义失败长什么样。我建议所有接口都使用统一的数据结构包裹返回内容。
{
"code": 0, // 业务状态码,0表示成功
"message": "ok", // 人类可读的描述
"data": { ... } // 真实业务数据
}
出错的时候也很明确:
{
"code": 40001,
"message": "参数校验失败:用户ID不能为空",
"data": null
}
- 状态码规范:HTTP状态码 + 业务状态码。HTTP状态码只表达传输层(200成功、400客户端错误、500服务端错误),业务状态码表达业务逻辑。
- 错误信息:对程序员友好,对用户友好(分场景提供)。
- 分页返回结构统一:
{
"code": 0,
"data": {
"items": [...],
"total": 100,
"page": 1,
"size": 20
}
}
4.4 RESTful vs RPC 风格选择
选哪个?没有银弹。我踩过不少坑总结出来下面这张表:
| 对比维度 | RESTful | RPC |
|---|---|---|
| 语义表达 | 资源 + 操作 | 方法名 + 参数 |
| 缓存友好 | 是(HTTP缓存) | 否 |
| 性能 | 中等(需解析URL) | 高(二进制序列化) |
| 学习成本 | 低(遵循Web标准) | 中等(需了解IDL) |
| 典型场景 | 对外API、Web应用 | 内部微服务、网关 |
注意:不要在同一套SDK里混用两种风格。我见过有人前半段用RESTful,后半段用RPC,结果调用方搞不清楚该用GET还是该传对象。风格一致比风格完美更重要。
4.5 一张图看懂接口设计规范
4.6 避坑指南
我这几年在接口设计上踩过的坑,总结成几条给你参考:
- 命名不要变:一旦发布,接口名和路径就再也不要改了。改一次,所有客户端都要跟着升级SDK。我曾经在一个开放平台,把
getUser改成了fetchUser,结果被合作伙伴投诉了一个月。 - 返回值结构不要中途变:新增字段可以,但不要改已有字段的类型、含义或位置。特别是不要从数组改成对象,或者从字符串改成数字——这些破坏性改动会让序列化直接崩溃。
- 错误信息要稳定:不要在错误信息里带数据库字段名、堆栈信息。统一返回可读的业务描述,并且为每个错误定义固定的业务码。
- 版本控制要提前做:接口路径或请求头里带上版本号(如
/v2/users),这样即使后面要大改,旧版本也能继续运行一段时间。
一个小建议:设计接口的时候,把自己想象成SDK的使用者。你能一眼看明白这个接口怎么调用吗?你的参数列表会不会让IDE提示太长?你的错误信息能不能帮问题定位?想清楚这些,规范自然就到位了。