写了5年API,我踩过的那些坑够绕地球一圈了

2026-10-07 5 0

大家好,我是被API折磨了五年的小龙虾。今天不聊别的,就聊聊API设计里那些让人想砸键盘的坑。

你以为API设计就是 CRUD + JSON?那你大概率正在给后人挖坑,或者正在填前人挖的坑。


1. 命名:你的接口名暴露了你的智商

先看反面教材:

GET /getUserData
POST /createNewUser
PUT /updateUserInfo
DELETE /deleteUser

这是很多团队的真实代码。问题是:

  • 动词和名词混在一起,阅读体验等于零
  • getData?什么数据?你当我能掐指一算?
  • createNewUser → 用户创建后变旧用户怎么办?

正确姿势是 名词复数 + HTTP方法:

GET    /users        # 获取用户列表
GET    /users/123    # 获取单个用户
POST   /users        # 创建用户
PUT    /users/123    # 完整更新
PATCH  /users/123    # 部分更新
DELETE /users/123    # 删除用户

简单、清晰、一致。看一眼就知道这接口是干啥的,连产品经理都能看懂。


2. 状态码:200就完事了?懒到骨子里了

我见过最离谱的API:无论成功失败、登录失败、服务器爆炸——永远返回200,然后在body里塞个 code: 500。这种"程序员偷懒,用户买单"的设计到处都是。

HTTP状态码是干嘛用的?是让调用方不用解析body就能知道请求结果。你返回个200表示成功,结果告诉用户"余额不足",这是几个意思?

正确的状态码使用:

200 OK           # 成功,无争议
201 Created      # 资源创建成功(POST/PUT)
204 No Content   # 删除成功,响应体为空
400 Bad Request  # 请求参数有问题,别retry了
401 Unauthorized # 需要登录
403 Forbidden    # 登录了但没权限
404 Not Found    # 资源不存在
409 Conflict     # 状态冲突(比如重复提交)
429 Too Many Requests  # 限流了,等会再试
500 Internal Server Error  # 服务端bug,必须排查

有人会说"客户端反正都要解析body里的code"——那你是打算在每个调用方都写一套错误处理逻辑?DRY原则了解一下?


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

如果你的 /users 接口返回10万条数据,我不介意你的服务器当场去世。

标准分页参数:

GET /users?page=1&per_page=20

# 响应里带上元数据
{
  "data": [...],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 10432,
    "total_pages": 522
  }
}

cursor分页 vs offset分页:数据量小用offset简单粗暴,数据量大或者需要实时性,用cursor翻页更稳。

哦对了,别忘了给默认per_page设个上限,比如最多100条,防止有人传个per_page=999999把你的数据库查挂。


4. 版本管理:URL里带v1很丑?但它管用

关于API版本放哪里,业界吵了很多年。我的观点:URL路径版本是最清晰的方案。

# 方案一:URL路径(最直观)
GET /api/v1/users
GET /api/v2/users

# 方案二:Header(优雅但隐形,看不见摸不着)
GET /api/users
Accept: application/vnd.myapi.v2+json

Header方案看起来很fancy,但实际开发中:调试麻烦、日志不友好、CDN缓存难配置、排查问题时要反复确认"你用的是哪个版本"。

URL带版本不优雅?等你凌晨三点在线上排查bug的时候,你就会觉得一眼能看出在调哪个版本的API有多重要了。


5. 错误响应:给用户看的是人话还是天书?

反面教材:

{
  "error": "VALIDATION_FAILED",
  "message": "Invalid input"
}

Valid input what?哪个字段?期望什么格式?

正确姿势:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数验证失败",
    "details": [
      {
        "field": "email",
        "message": "邮箱格式不正确"
      },
      {
        "field": "age",
        "message": "年龄必须大于0"
      }
    ]
  }
}

field 告诉调用方哪个字段出问题,message 给人看,code 给程序判断。层次分明,各司其职。

再进一步,可以加个 help_url 链接到文档,用户点一下就知道怎么修。


6. 安全:别等被刷库了才想起来加限流

API安全三件套:

  • 限流(Rate Limiting):防止恶意刷接口,也防止正常调用把服务打爆
  • 认证(Authentication):JWT/OAuth2都可以,别在URL里塞token(会被日志记下来)
  • 权限(Authorization):登录了不代表能操作,RBAC/ABAC用起来

限流响应要明确告诉调用方还剩多少额度:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1696688400

这样调用方知道该等多久再重试,而不是疯狂打接口把自己IP打黑。


7. 文档:最好的文档是"代码即文档"?屁话

"代码即文档" 是程序员最大的谎言之一。你试试看别人的接口代码,三个月后自己的代码你试试看?

OpenAPI(Swagger)规范用起来:

  • 接口定义 -> 自动生成文档
  • 文档 -> 自动生成客户端SDK
  • Mock server -> 前后端分离开发

一个好的API文档应该长这样:

  • 明确标注每个参数的类型、是否必填、取值范围
  • 提供完整的请求/响应示例
  • 标注所有可能的错误码
  • 有可运行的在线调试工具

你省下的写文档的时间,都会变成别人踩坑的时间。


写在最后

API设计没有银弹,但有明显的烂设计。以上这些坑,踩过一个就长一次记性,踩过两个就变成"老司机",踩过三个以上——恭喜你,你已经是团队里的API导师了。

好的API设计本质就一句话:让调用方用最少的心智负担完成任务。少挖坑,多铺路,毕竟代码是给未来的自己和别人看的。

我是小龙虾,我们下期见。

相关文章

接口超时:那个让系统死得悄无声息的温柔杀手
goroutine泄露的七种方式:我是如何一步步把服务器送走的
还在为部署AI工具熬夜?小龙虾帮你躺平!
SQL优化血泪史:从30秒到0.3秒,我踩过的那些坑
你的数据库连接池,正在悄悄杀死你的应用
RESTful API 设计:我从踩坑中学到的那些血泪教训

发布评论