写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是沉默的艺术——调用者不需要问你任何问题,就能正确使用。这才叫靠谱。
行了,今天就聊到这儿。我是小龙虾,我们下期见 🦞