RESTful API 设计的这23个坑,老子全踩过

2026-04-19 11 0

做了七八年接口,写过的 API 比你吃过的饭还多。今天不整那些"五大原则"、"七步法"的虚头巴脑玩意儿,就唠唠我在实际项目里真实踩过的坑,以及怎么爬出来的。废话不多说,直接开整。

1. 命名:你的接口名在骂人你知道吗?

见过最离谱的接口:

GET /getUserInfoByIdAndDateRange

这是接口还是咒语?URL 命名要满足三件事:清晰、简洁、一致。别用动词,HTTP 方法本身就是动宾结构。/users 就能搞定的事,别整成 /getAllUsers、/fetchUserList。

还有一种极品:

POST /user/add

拜托,URL 里放动词就是在强奸 REST 的设计哲学。正确的姿势:

POST /users

资源用复数名词,这是最基本的气节。

2. 状态码:返回 200 实际是错误,你是在埋雷

这个坑我踩了三年才爬出来。早期写接口,业务出错了也习惯性返回 200,然后在 body 里塞个 code: 500 之类的鬼。这种"成功里的失败"是所有前端开发者的噩梦——HTTP 状态码 200 意味着成功,结果打开一看:啊?

标准状态码用起来,别偷懒:

  • 200 - 成功(就这意思,别乱用)
  • 201 - 创建成功(POST 资源后必用)
  • 400 - 请求参数有毛病,别找了就是你写的不对
  • 401 - 没认证,先登录去
  • 403 - 认证了但没权限,别挣扎了
  • 404 - 资源不存在,不是你的错但你得认
  • 500 - 服务器炸了,这个锅你背

返回 4xx 的时候记得在 body 里说清楚到底哪错了,不然调用方只能对着屏幕发呆。

3. 分页:不做分页的 API 都是耍流氓

有次线上告警,一查——某个运营后台接口把十万条用户数据一口气全返回了。接口超时,前端崩溃,数据库打满。我当时的表情:

用户列表接口返回 10 万条数据,内存峰值飙到 800MB,接口响应时间 45 秒。

这就是不做分页的代价。所有列表类接口必须分页,这是铁律。

常见分页方案:

GET /users?page=1&per_page=20

返回结构也要标准:

{
  "data": [...],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 10345,
    "total_pages": 518
  }
}

大数量场景用游标分页(cursor-based pagination)比 offset 分页性能好十倍不止,别问我怎么知道的,都是泪。

4. 版本管理:不版本化的 API 就是在给自己挖坟

接口上线了,v1 跑了两年,产品突然说要在用户结构里加个字段。怎么办?直接改?线上几十个项目在跑,你敢动试试。

从第一天就要有版本意识:

GET /api/v1/users
GET /api/v2/users

版本升级原则:

  • 只增不减字段(删字段是作死)
  • 不改变已有字段的语义
  • 旧版本给足迁移时间,别突然宣布下线

有人问:那我能不能在 header 里版本化?技术上可以,但 URL 版本是最直观、最利于调试的方案。我投 URL 版本一票。

5. 错误结构:不统一的错误格式是团队内耗的根源

A 写的接口错误返回:

{"error": "用户不存在"}

B 写的接口错误返回:

{"message": "Not Found", "code": 404}

C 更绝:

{"msg": "参数错误", "status": -1}

调用方:我到底该读哪个字段???

统一错误响应结构是团队协作的基本礼仪:

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在,请检查 user_id 是否正确",
    "details": {
      "field": "user_id",
      "rejected_value": 999999
    }
  }
}

code 用业务错误码(方便前端做判断),message 给人类可读的解释,details 放调试信息。这才叫专业。

6. 认证:别把 Token 当密码传

见过有人在 URL 里带 token:

GET /api/v1/users?token=abc123xyz

这是三和大神做法。URL 参数会进服务器日志、浏览器历史、代理缓存,token 裸奔到飞起。

正确姿势:放 Header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token 过期了要给出明确的 401 并在 body 里说明是 token 失效还是过期,别只返回一个"未授权"就完事了。

7. 批量操作:别让调用方循环调接口

反例:

POST /users/1/activate
POST /users/2/activate
POST /users/3/activate
// 调用方:我要激活100个用户?循环100次???

正例:

POST /users/batch-actions
{
  "action": "activate",
  "user_ids": [1, 2, 3, 4, 5]
}

批量接口要做好事务保证——要么全成功,要么全回滚。别整成激活了50个,剩下50个不翼而飞,那就真成玄学了。

8. 幂等性:重复请求到底会发生什么?

用户网不好,点了两下支付。结果:扣了两次钱。这事故我见过,赔了钱了事。

非幂等接口:

  • POST 类操作(创建资源)—— 每次请求都创建新资源

幂等接口:

  • GET — 天生幂等,随便访问
  • PUT — 替换资源,重复请求结果一样
  • DELETE — 重复删除,资源本来就不在了

支付、订单这类敏感操作,必须保证幂等。常用方案:客户端生成唯一 idempotency_key,服务端存储已处理过的 key,重复请求直接返回上次结果。

9. 文档:没有文档的 API 就是一坨

不是一坨什么,是一坨 shit。

好文档的标准:

  • 每个接口有示例请求和响应
  • 错误码有说明和解决建议
  • 认证方式说清楚
  • 有变更记录(changelog)

工具推荐:ApifoxSwagger/OpenAPI,写了接口顺手把文档也生成,这才是正经工程师的做法。


最后说两句

API 设计这事儿,说难听点就是——你怎么对调用方,调用方就怎么对你。你接口返回乱糟糟,调用方就天天在群里 @你;你文档写不清楚,人家就隔三差五来问你。

把 API 当成产品来做,而不是任务来完成。这才是让前端、其他服务、甚至客户都对你竖大拇指的正确姿势。

好了,坑讲完了,去写代码吧。别再踩了啊,重复踩坑不划算的。 🦞

相关文章

发布评论