REST API 最容易翻车的 10 个地方,我都替你踩过了
做后端开发这么多年,我见过太多项目在 API 设计上翻车。有些是新手没经验,有些是老油条太懒。归根结底,API 是产品的门面,设计烂了,后期怎么填都填不完。
今天不整虚的,直接上硬菜。我把 REST API 设计中最容易出问题的 10 个地方全列出来,逐个分析,配上真实案例。看完你可能会觉得:**我怎么全中了?**
1. 命名随心所欲,URL 乱成一锅粥
这个问题太常见了。看看这些 URL:
GET /getUser?id=123
POST /user/add
GET /fetchUsersByType?type=1
这是做增删改查还是写散文?RESTful 的精髓在于**资源 + 操作**的统一。正确的姿势:
GET /users/123 # 获取单个用户
POST /users # 创建用户
PUT /users/123 # 更新用户
DELETE /users/123 # 删除用户
名词用复数,动词从 URL 里滚蛋。HTTP 方法本身就是动宾结构,别画蛇添足。
还有一个大坑:路径层次太深。`/orgs/{org_id}/teams/{team_id}/members/{member_id}` 这种嵌套超过两层就该反思了。你的 API 不是文件系统,别套文件夹的逻辑。
2. 状态码乱用,返回格式天差地别
见过最离谱的:一个接口成功返回 200,body 是 `{"code": 0, "msg": "success"}`;另一个返回 200,body 是 `{"status": "ok", "data": {...}}`;还有一个返回 200,body 直接是数组 `[]`。
200 是个筐,什么都往里装?**这叫懒,叫没标准,叫给自己挖坑。**
状态码要规范:
200 OK # 成功
201 Created # 资源创建成功
204 No Content # 删除成功,无返回体
400 Bad Request # 请求参数有问题
401 Unauthorized # 没登录
403 Forbidden # 没权限
404 Not Found # 资源不存在
422 Unprocessable Entity # 语义错误,参数格式对但逻辑错
500 Internal Server Error # 服务端崩了
统一响应格式也必须定死。建议:
{
"code": 0, // 业务状态码,0=成功
"message": "success",
"data": {...}
}
最怕的是有的接口返回 code 有的返回 status 有的返回 success,调试的时候你想砸键盘。
3. 分页参数各玩各的
翻页这个事儿,十个后端能整出十二种参数名:
page + pageSize
offset + limit
start + count
pageNum + pageSize
skip + take
文档里写的是 page=1&pageSize=10,实际请求的是 offset=0&limit=10,线上直接血崩。
**分页参数必须统一,而且要写死在文档里。** 推荐用:
GET /users?page=1&page_size=20
返回也要统一:
{
"data": [...],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1000,
"total_pages": 50
}
}
别搞 pageSize 和 page_size 混用的幺蛾子,下划线还是驼峰,从一而终。
4. 不知道什么时候该用 GET,什么时候该用 POST
这个问题说小不小,说大不大,但踩坑的人真不少。
GET 是**幂等且安全**的——查多少遍都不会改变数据,而且只应该用来获取资源。POST 是**非幂等**的——用来创建资源,或者做那些"有副作用"的操作。
最常见的错误:用 GET 干所有事。
GET /users/login?username=xxx&password=yyy # 大忌!
GET /users/export?format=csv # 数据量大了试试?
登录有副作用(写 session、写登录日志),导出可能触发大量计算,这些都应该用 POST。
简单判断标准:**这个操作会不会修改服务器状态?** 会就用 POST,不会就用 GET。查询字符串里放密码是什么鬼?
5. 错误信息形同虚设
很多 API 的错误返回是这样的:
{
"message": "操作失败"
}
操作失败?什么操作?哪里失败了?为什么失败?前端工程师看到这种东西,想骂人。
好的错误信息应该包含:
{
"code": 1001,
"message": "用户名已存在",
"field": "username",
"request_id": "abc123"
}
把错误信息当成给开发者看的说明书,而不是敷衍的客套话。code 要有明确含义,message 要说人话,field 要告诉前端哪个字段出了问题,request_id 方便后端查日志。
你调试生产问题的时候,就会感谢当年写详细错误信息的自己了。
6. API 版本管理形同虚设
很多项目一开始没版本概念:
GET /users
GET /items
GET /orders
然后业务变了,需要破坏性改版,这时候就傻眼了——老客户端还在跑,新版 API 怎么上?
版本管理要**从第一天就定下来**,别等出问题了再打补丁。两种常见方案:
# URL 路径版本(直观,REST 社区主流)
GET /v1/users
GET /v2/users
# Header 版本(干净,但不够直观)
GET /users
API-Version: 2024-01-01
我个人倾向 URL 版本。调试方便,一眼就知道调的是哪个版本。改动代价大,团队才会慎重对待 API 变更。
还有一点:**同一个 major 版本不能破坏性变更。** v1 加字段可以,减字段不行,改字段含义更不行。想清楚了再发版。
7. 不知道什么时候该拆 API,什么时候该聚合
这是经验活,最容易走极端。
**拆太细**:
GET /user/profile
GET /user/settings
GET /user/stats
GET /user/permissions
BFF(Backend For Frontend)层还好,如果是给第三方客户端用,光一个用户信息要调四个接口,网络开销、移动端电量,都是钱。
**合太粗**:
GET /users?include=profile,settings,stats,permissions,orders,comments,followers
include 参数爆炸,SQL 关联十几张表,一个接口干到 3 秒,缓存都没法做。
我的经验:**按业务边界聚合,按调用频率优化。** 同一个业务域的用同一套接口;被高频同时调用的字段,初期可以冗余进同一个接口,等遇到性能问题了再拆。
8. 认证鉴权一锅炖
认证(Authentication)和鉴权(Authorization)不是一回事,但太多人混着来。
认证是回答"**你是谁**",鉴权是回答"**你能干什么**"。很多 API 用一个 token 解决所有问题,爽是爽了,但业务复杂了就兜不住。
比如:一个后台管理系统的普通管理员,调用 `/users` 可以,但调用 `/users/delete` 应该被拒绝。如果只有一个 token,没有权限校验逻辑,你就只能在每个接口里写 if-else。
正确的做法:用 JWT 做认证(你是谁),用 RBAC/ABAC 做鉴权(你能干什么)。分层清晰,逻辑才不容易乱。
# 请求头带 token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# 后端解析 token 拿到用户 ID,查权限表判断能不能操作
9. 忽略 API 的可发现性(Discoverability)
好的 API 应该能"自描述"。什么意思?
看这个设计:
GET /orders # 返回订单列表
# 看响应里的 link header
Link: <http://api.example.com/orders?page=2>; rel="next",
<http://api.example.com/orders?page=5>; rel="last"
前端不需要自己算分页 URL,API 直接告诉你下一页、最后一页在哪。这就是**超媒体控制(Hypermedia Control)**,HATEOAS 的核心。
很多人觉得这个太复杂用不上。但实际上,你只需要返回一个 `links` 字段:
{
"data": [...],
"links": {
"next": "/orders?page=2",
"prev": "/orders?page=1"
}
}
这对前端和第三方开发者非常友好。文档不全的时候,至少能从响应里推断出怎么继续操作。
10. 文档和代码老死不相往来
这是最大的雷。代码改了一版,接口参数变了,文档没人更新;或者文档写了,功能压根没上线。
我见过最夸张的:一个接口的文档写着"必填字段:name, email, phone",实际代码里 phone 从来没用到过,email 的校验规则也和文档不符。这种 API 谁能接?
解决方案:**文档即代码,代码即文档。**
用 OpenAPI/Swagger 从代码注释自动生成文档,用 Postman 或 Insomnia 导出一致性测试用例。文档要进 Code Review,改接口不更新文档 = 代码没写完。
我现在的团队要求:PR 描述里必须包含 API 变更说明,没有这个的一律打回。习惯以后,文档和代码终于能保持同步了。
写在最后
API 设计这东西,说到底是工程经验和审美判断的结合。懂 HTTP 规范是基础,但光懂规范不够——你得知道什么样的接口让调用者舒服,什么样的返回让调试的人少掉头发。
这篇文章里提到的 10 个问题,每一个都是真实踩过的坑。希望你看完能少走几步弯路,毕竟——
**好的 API 设计是留给后人用的,烂的 API 设计是留给自己的。**
共勉。