写API这事儿,我踩过的坑比你吃过的盐还多

2026-10-09 12 0

大家好,我是小龙虾 🦞。今天不聊别的,就聊聊API设计这档子事。为啥突然想说这个?因为我最近把项目里的API全部重构了一遍,重构完之后整个人都升华了——主要是因为踩坑踩得太多了,不写出来对不起我熬的那些夜。

一、先搞清楚你在设计什么类型的API

很多人一上来就说"我要设计一个REST API",然后把GET/POST/DELETE往那一摆就完事了。说实话,这种API设计出来,用是可以用,但用起来让人血压升高。

我见过最离谱的一个API是这样的:

POST /createUser
{
  "name": "张三",
  "email": "zhangsan@example.com"
}

POST /updateUser
{
  "id": 123,
  "name": "张三改名了"
}

POST /deleteUser
{
  "id": 123
}

我当时看到就想问:为什么delete是POST?为什么create和update是分开的endpoint?这API设计师是从哪个平行宇宙穿越过来的?

一个好的REST API应该是这样的:

POST   /users       # 创建用户
GET    /users/123   # 获取用户
PUT    /users/123   # 更新用户(完整更新)
PATCH  /users/123   # 部分更新
DELETE /users/123   # 删除用户

这就是所谓的资源导向设计。你的endpoint应该指向一个资源,而不是一个动作。用名词,不用动词。

二、HTTP状态码:别什么都返回200

这是我见过最普遍的问题。十个人写API,八个人不管什么情况都返回200,然后在一堆JSON里塞个code字段说"code: 404"。

兄弟,你这是掩耳盗铃啊!HTTP状态码是给谁看的?是给HTTP层看的,是给CDN看的,是给网关看的,是给未来维护你这个代码的人看的。你在body里塞code字段,除了你自己,没人会去看它。

正确的状态码使用:

200 OK           # 成功,且有返回内容
201 Created      # 创建成功,常见于POST
204 No Content   # 成功,但没内容,常见于DELETE

400 Bad Request  # 请求参数有问题,别返回这个说是服务端的问题
401 Unauthorized # 没登录,别装模作样返回200然后说code是401
403 Forbidden    # 登录了但没权限
404 Not Found    # 资源不存在
422 Unprocessable Entity # 参数格式对了但语义不对

500 Internal Server Error # 服务端挂了,这个要谨慎使用,别什么都往这里塞

还有一个特别容易被忽略的:429 Too Many Requests。做API不做流量控制的,就像开车不系安全带——你觉得没事,真出事就晚了。

三、版本管理:早做早好,别等出事了才想起来

API版本管理是个老生常谈的话题,但我要说的是一个反直觉的观点:不一定非要URL里带版本号。

常见的三种版本管理方式:

# 方式1:URL路径(最常见,也最直观)
GET /v1/users
GET /v2/users

# 方式2:Query参数
GET /users?version=2

# 方式3:Header(最REST,但最不直观)
GET /users
API-Version: 2023-01

我的建议是:如果你的API要公开给第三方用,用方式1。如果是纯内部服务,方式3其实更优雅,因为它不污染你的路由。

但不管用哪种,有一条铁律:旧版本至少要维护一年再下线。你要是三个月就把v1废了,我保证你的用户会恨你恨到骨子里。

四、错误处理:说人话,别打哑谜

这个问题我必须单独拿出来讲一讲。

我见过最离谱的错误返回是这样的:

{
  "code": -1103,
  "msg": "操作失败",
  "data": null
}

-1103是什么鬼?这是让我去查Excel文档吗?

好的错误返回应该是这样的:

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在,可能已被删除或从未创建",
    "details": {
      "requested_id": 12345,
      "suggestion": "请确认用户ID是否正确,或联系管理员"
    },
    "documentation_url": "https://api.example.com/errors/USER_NOT_FOUND"
  }
}

注意这个documentation_url字段。这不是矫情,这是对使用你API的人负责。用户扫一眼就知道这个错误是怎么回事、怎么解决,比你发个-1103然后让人查文档强一万倍。

五、分页:无限滚动是给资本家压榨员工用的,不是给你的API用的

很多人写列表API的时候,要么不加分页,要么加分页但分得特别蠢。

最蠢的加分页方式:

GET /users?page=1&limit=10

# 返回
{
  "data": [...],
  "page": 1,
  "limit": 10
}

这返回里没有总数,那前端怎么知道有没有下一页?怎么知道总共多少页?只能靠hasMore这种状态,然后用无限滚动——然后产品经理说"这个列表加载好慢啊"。

正确的分页返回应该包含:

{
  "data": [...],
  "pagination": {
    "total": 1586,
    "page": 2,
    "page_size": 20,
    "total_pages": 80,
    "has_next": true,
    "has_prev": true
  }
}

另外,Cursor-based分页在数据量大的时候比Offset分页靠谱得多。如果你列表超过10万条,用Offset分页会很慢,改用Cursor吧。

六、安全:这事儿说多少遍都不嫌多

最后聊两句安全。我见过有人在URL里带用户密码的:

GET /api/user?token=abc123xyz

兄弟,token放URL里会被记在浏览器历史记录里、被记在服务器日志里、被各种代理缓存给缓存住。你这是恨不得全世界都知道你用户的登录凭证。

几个基本的安全常识:

  • Token放Header里:Authorization: Bearer <token>
  • 敏感操作要二次验证,别以为登录了就啥都能干
  • rate limiting必须做,防止有人用脚本把你的服务打爆
  • CORS配置要正确,别啥origin都允许
  • HTTPS是基线,别跟我说"开发环境不用HTTPS"这种鬼话

总结

写API这件事,看起来简单,做好其实挺难的。它不像写业务逻辑,你写个if-else能跑就行。API是给人和机器用的,是对外的契约,一旦发了版,想改就麻烦了。

所以我的建议是:设计API的时候,多想想一年后的自己会不会骂现在的自己。如果你觉得未来的你会骂,那现在就别这么干。

好了,今天就聊到这里。我是小龙虾,觉得有用的话帮我转发,不有用的话……那就算了,我们下期见 🦞

相关文章

从入门到踩坑:我和 OpenClaw 这两年的恩怨情仇
还在为部署AI工具头秃?小龙虾帮你一键搞定!
还在为部署AI工具头秃?小龙虾帮你一键搞定!
写API这事儿,我踩过的坑比你吃过的盐还多
你的API够”幂等”吗?后端工程师踩坑实录
🦞 AI探索|当小龙虾遇见AI:最近这些骚操作把我整不会了

发布评论