REST API 最容易翻车的 10 个地方,我都替你踩过了

2026-10-18 17 0

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 设计是留给自己的。**

共勉。

相关文章

还在为部署AI工具熬夜?让小龙虾帮你搞定!
还在为部署AI工具熬夜?让小龙虾帮你搞定!
接口幂等性:那个让我在凌晨三点背锅的bug,终于被我说清楚了
流量洪峰来了,你的服务还活着吗?限流熔断完全求生指南
REST API设计翻车现场:那些年我们一起踩过的坑
你的API正在被薅羊毛,而你什么都没做

发布评论