RESTful API 设计规范:资源命名、方法语义、状态码与版本管理
说实话,我见过太多接口设计,一眼就能看出是新手还是老手写的。不是代码能力问题,而是对规范的理解深度。RESTful API 设计,说白了就是一套约定。大家按这个约定来,沟通成本就低。不按约定来,每次联调都是灾难。
我个人习惯,在设计接口之前,先想清楚三件事:资源是什么、操作怎么表达、异常怎么处理。这三点想明白了,接口质量就稳了八成。
资源命名规范:名词复数 + 小写 + 连字符
资源命名是 API 的门面。我见过用动词命名的、用中文拼音的、甚至用下划线加驼峰混搭的。嗯,这些我都改过。
核心原则就一条:用名词复数表示资源集合。
正确示例:
/users— 用户集合/orders— 订单集合/blog-posts— 博客文章集合
错误示例:
/getUser— 混入动词/UserInfo— 大写开头/get_all_orders— 下划线 + 动词
为什么用复数?因为资源集合是容器,单个资源是容器里的元素。你想想看,/users/123 比 /user/123 更自然——先找到用户集合,再定位到具体用户。
命名规则我总结了一张表:
| 规则 | 说明 | 示例 |
|---|---|---|
| 全小写 | 避免大小写敏感问题 | /api/users 而非 /api/Users |
| 连字符分隔 | 替代下划线或驼峰 | /blog-posts 而非 /blog_posts |
| 名词复数 | 表示资源集合 | /orders 而非 /order |
| 层级用斜杠 | 表达从属关系 | /users/123/orders |
| 避免动词 | 动作由 HTTP 方法表达 | 用 POST /orders 而非 /createOrder |
小技巧:如果某个操作确实无法映射到标准 CRUD,比如「批量审批」或「重新计算」,可以考虑用 /orders/batch-approve 这种形式。但记住,这是例外,不是常态。我在项目中遇到超过 80% 的「特殊操作」,其实都能用标准方法解决。
HTTP 方法语义化:GET/POST/PUT/PATCH/DELETE
HTTP 方法就是你的动词。每个方法都有明确的语义,别乱用。
我曾经接手过一个项目,所有操作都用 POST,包括查询。你想想看,查询接口用 POST,缓存怎么搞?幂等怎么保证?后来重构时,光改这个就花了三天。
| 方法 | 语义 | 幂等 | 请求体 | 典型场景 |
|---|---|---|---|---|
GET |
查询资源 | 是 | 无 | 获取用户列表、获取订单详情 |
POST |
创建资源 | 否 | 有 | 创建用户、提交订单 |
PUT |
全量替换 | 是 | 有 | 更新用户全部信息 |
PATCH |
部分更新 | 否 | 有 | 修改用户邮箱 |
DELETE |
删除资源 | 是 | 无 | 删除用户、取消订单 |
这里有个容易踩的坑:PUT 和 PATCH 的区别。PUT 是全量替换,你传什么字段,服务端就用什么字段覆盖。PATCH 是部分更新,只修改你传的字段。
避坑指南:我曾经在项目中用 PUT 做部分更新,结果前端只传了邮箱字段,后端把用户的姓名、手机号全清空了。那次事故之后,我定了一条铁律:PUT 必须传完整资源,PATCH 才做部分更新。
状态码的正确使用:别什么都返回 200
状态码是 API 的「表情」。你用对了,调用方一看就懂。用错了,联调时全是「为什么报错?」。
我见过最离谱的:所有请求都返回 200,然后在响应体里放一个 code 字段表示成功或失败。你想想看,这跟脱裤子放屁有什么区别?HTTP 协议本身就提供了状态码,为什么不用?
常用的状态码其实不多,记住这几个就够了:
| 分类 | 状态码 | 含义 | 使用场景 |
|---|---|---|---|
| 成功 | 200 OK |
请求成功 | GET、PUT、PATCH 成功返回 |
201 Created |
创建成功 | POST 创建资源后返回 | |
204 No Content |
删除成功 | DELETE 成功后返回 | |
| 客户端错误 | 400 Bad Request |
请求参数错误 | 参数校验失败、格式错误 |
404 Not Found |
资源不存在 | 查询不存在的用户 | |
409 Conflict |
资源冲突 | 重复创建、版本冲突 | |
| 服务端错误 | 500 Internal Server Error |
服务器内部错误 | 未捕获的异常 |
503 Service Unavailable |
服务不可用 | 限流、熔断、维护中 |
我的习惯:错误响应体统一格式,方便调用方解析。
{
"error": {
"code": "USER_NOT_FOUND",
"message": "用户 123 不存在",
"details": "请检查用户 ID 是否正确"
}
}
版本管理策略:别让旧接口拖垮你
接口一定会变。不加版本管理,就是给自己埋雷。
我记得有一次,一个核心接口改了响应结构,结果下游十几个服务全部报错。那次之后,我强制要求所有接口必须带版本号。
常见的版本管理方式有三种:
| 方式 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| URL 路径 | /api/v1/users |
直观、容易路由 | URL 变长、版本扩散 |
| 请求头 | Accept: application/vnd.myapp.v1+json |
URL 干净 | 调试不方便、文档不直观 |
| 查询参数 | /api/users?version=1 |
实现简单 | 容易被忽略、缓存不友好 |
我个人推荐 URL 路径方式。虽然有人觉得它「不够优雅」,但实际项目中,直观比优雅更重要。你想想看,运维同学排查问题时,一眼就能看出请求走的是哪个版本。
版本管理原则:
- 向后兼容:v1 的接口不能因为 v2 上线就挂掉
- 废弃周期:提前通知,给下游至少 3 个月的迁移时间
- 最小版本数:同时维护不超过 2 个版本,旧版本到期强制下线
知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作设计 API 时的检查清单。
本章要点回顾:
- 资源命名:名词复数、全小写、连字符分隔
- HTTP 方法:GET 查、POST 建、PUT 全量改、PATCH 部分改、DELETE 删
- 状态码:200/201/204 表示成功,400/404/409 表示客户端错误,500/503 表示服务端错误
- 版本管理:推荐 URL 路径方式,保持向后兼容,控制废弃周期