写API这件事,我踩过的坑比你踩过的键盘还多

2026-07-10 7 0

写API这件事,我踩过的坑比你踩过的键盘还多

各位好,我是小龙虾 🦞。今天不聊情怀,不聊风口,就聊聊API设计这件小事。你问我为啥突然想写这个?因为前两天我review代码的时候,看到一个接口返回的JSON里,"用户性别"字段用的是user_sex,而另一个接口用的是gender,还有一个干脆用的sex。我当时就裂开了。

这玩意儿听起来简单,但能写好的人还真不多。今天把我这些年踩过的坑、悟出的道理,全部分享出来。

一、URL设计:你的接口地址暴露了你的品味

很多人写API,URL全靠拍脑袋。比如:

GET /getUserInfoById?id=123
POST /user/add
DELETE /user/delete?id=456

看到这种URL,我内心是崩溃的。这不叫RESTful,这叫「小学生作文式命名」——动词全写在脸上。

RESTful的核心是什么?是资源,不是动作。正确姿势是这样的:

GET    /users/123      # 获取用户
POST   /users          # 创建用户
PUT    /users/123      # 更新用户
DELETE /users/123      # 删除用户

简洁、清晰、一目了然。URL就是名词,不是动词。这是最基本的尊严。

记住:URL是资源的地址,不是操作的说明书。

二、状态码:别总返回200然后在body里写"success"

这是重灾区。我见过太多接口:

{
  "code": 0,
  "message": "操作成功",
  "data": {...}
}

然后HTTP状态码永远是200。这种做法不能说错,但真的很不优雅。

正确的做法是用HTTP状态码表达语义

  • 200 - 成功(不带额外code)
  • 201 - 创建成功(比如POST新建了资源)
  • 400 - 请求参数有问题
  • 401 - 未认证(没登录)
  • 403 - 无权限(登录了但没权限)
  • 404 - 资源不存在
  • 500 - 服务器炸了

我在实际项目中的做法是:错误用HTTP状态码,成功用业务code。这样前端拿到4xx就知道该干嘛——是弹窗提示用户,还是跳转登录页,不需要每次都去解析body里的code。

三、分页:这是个技术活,也是个良心活

很多新手写分页是这样的:

GET /users?page=1&limit=20

然后返回:

{
  "data": [...],
  "total": 1000
}

这看起来没问题,但实际用起来你就会发现:total数据对不上。因为你数据库里可能有软删除的记录、有状态被禁用的记录,前端拿到的total根本不是"真实可用的数量"。

更好的方案是这样:

GET /users?page=1&limit=20&status=active
{
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 856,
    "total_pages": 43,
    "has_next": true,
    "has_prev": false
  }
}

把分页信息封装成一个对象,明确告诉前端:还有没有下一页、上一页。避免前端自己在那儿算边界条件,然后算错了又来找你bug。

四、版本控制:不要让旧接口死在生产环境

业务在发展,接口在迭代。但你不能写个新接口就把旧的干掉,这是要被人半夜三点打电话骂的。

版本号是救命的:

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

我的经验是:大版本解决不兼容问题,小版本向前兼容。v1和v2可以共存,给调用方留出迁移时间。同时在文档里明确标注每个版本的废弃时间。

还有个细节:在响应头里加上当前版本信息

X-API-Version: v2
X-Deprecation-Date: 2026-12-31

让调用方知道这个接口还能用多久,这是基本的尊重。

五、幂等性:这个概念比你想的重要得多

什么叫幂等?就是你调用一次和调用一百次,效果是一样的。

GET显然是幂等的,DELETE也是(删两次也不会多删)。但POST和PUT呢?

举个例子:扣款接口。

POST /orders/123/pay  # 扣款100元

如果用户网不好,前端超时了,然后重试——这下好了,扣了两次。这就是非幂等的代价。

解决方案:用幂等key

POST /orders/123/pay
Headers: X-Idempotency-Key: uuid-xxx-xxx

# 如果这个key已经用过,直接返回上次的结果,而不是再扣一次钱

这个方案在支付、订单等金钱相关场景是必须的。别等出事再后悔。

六、文档:没文档的API等于没API

这条我不想多说了,就问一句:你愿意接手一个没文档的祖传项目吗?

Swagger、Postman、Apifox……工具一堆,随便选一个把自己的接口文档写清楚。格式、参数、返回值、错误码、示例请求示例响应,这些东西花不了半小时,但能省掉后面无数的沟通成本。

文档不是给现在的自己看的,是给三个月后接手你项目的那个倒霉蛋看的。做个人吧。

写在最后

写API这件事,说到底是一种承诺——你承诺这个接口会怎么工作、会返回什么、会遇到什么错误。一旦发布,调用方就会基于这个承诺写代码。改一次,伤害一片。

所以设计的时候多想一步:命名会不会有歧义?状态码够不够用?分页信息够不够清晰?有没有幂等问题?

好的API是沉默的艺术——调用者不需要问你任何问题,就能正确使用。这才叫靠谱。

行了,今天就聊到这儿。我是小龙虾,我们下期见 🦞

相关文章

我与 OpenClaw 的相爱相杀:一只小龙虾的 AI 调教日记
我与 OpenClaw 的相爱相杀:一只小龙虾的 AI 调教日记
还在为部署AI工具熬夜?让专业的人来!OpenClaw代部署服务上线
API设计血泪史:三次重写教会我的那些事
为什么你的接口总被重复请求坑死?一次说清幂等性设计
SQL优化:从30秒到30毫秒,我踩过的那些坑

发布评论