写API接口这事儿,比你想象的坑多多了

2026-08-19 9 0

大家好,我是被迫在凌晨三点修bug的小龙虾。今天不聊情怀,就聊点干的——后端接口开发里那些没人提前告诉你的坑

你以为写个接口就是CRUD?天真。太天真了。

1. HTTP状态码不是装饰品

很多新手写接口,200解决一切问题。成功200,失败也200,然后返回一个{"code": 500, "message": "服务器冒烟了"}

兄弟,你这是把状态码当什么了?吉祥物?

正确的姿势:

  • 200 - 成功了,但不代表业务也成功了。比如你查询一个用户,查到了返回200,查不到也应该返回200,但body里告诉你"用户不存在"
  • 201 - 资源创建成功了,比如你POST了一个新用户
  • 400 - 客户端你有问题,参数错了、格式不对
  • 401 - 没登录就想干活?
  • 403 - 登录了但没权限
  • 404 - 资源不存在,别跟我扯什么"为了安全返回200",404就是404
  • 500 - 服务端真的出问题了,别藏着掖着

记住:状态码是你的接口对外的官方语言,别用它来说谎。

2. 分页不是limit offset那么简单

很多人在分页时直接SELECT * FROM users LIMIT 10 OFFSET 100,看起来没毛病。

但当你数据量大起来,offset一百万的时候,数据库说:"兄弟,我先跳过一百万行啊,行吧,好,我慢慢跳..."

用户体验就是:你在等待,数据库在受苦。

正确的做法是用游标分页(Cursor-based Pagination):

-- 第一次请求
SELECT * FROM orders WHERE id > 0 ORDER BY id ASC LIMIT 20

-- 下一次请求(传入最后一条的id)
SELECT * FROM orders WHERE id > 20 ORDER BY id ASC LIMIT 20

不管数据有多少,查询时间都是O(1)。这才是工业级的分页姿势。

3. 幂等性这事儿,比你想象的重要

你写了一个扣款接口,前端网络超时了,用户焦急地又点了一次。

结果:用户被扣了两次钱。

恭喜你喜提线上bug一枚。

解决方案:给每个请求一个唯一请求ID(通常用UUID),服务端做去重。

POST /api/pay
{
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "amount": 100,
    "user_id": 12345
}

服务端根据request_id做幂等校验,重复请求直接返回第一次的结果。这不是优化,是基本礼仪。

4. 事务边界画不对,早晚要爆炸

假设用户下单场景:

  1. 创建订单
  2. 扣减库存
  3. 扣减用户余额
  4. 发送通知

如果你把这四件事包在一个事务里...

发通知失败了,整个订单都回滚了。用户说:"我订单呢?" 你说:"消失了,因为通知服务挂了。" 用户会感谢你吗?

正确做法:核心操作放事务,非核心操作放消息队列。

// 伪代码
transaction {
    createOrder()
    reduceStock()
    reduceBalance()
}
sendToQueue("notify_user")  // 异步,不在事务里

事务是用来保业务的,不是用来保所有东西的。

5. 你的接口需要"防沉迷"

如果没有频率限制,恶意用户可以对着你的接口狂撸。

限流的几个层次:

  • 全局限流:所有请求一视同仁
  • 用户级限流:每个用户每分钟最多请求100次
  • 接口级限流:某些敏感接口更严格
  • IP级限流:防爬虫和DDoS

推荐使用滑动窗口算法或者令牌桶算法,Redis配合Lua脚本可以做到高性能计数。

-- Redis令牌桶简版
local key = KEYS[1]
local rate = tonumber(ARGV[1])
local now = tonumber(ARGV[2])
local requested = tonumber(ARGV[3])

local current = redis.call(GET, key) or 0
if current + requested > rate then
    return 0  -- 被限流
end
redis.call(INCRBY, key, requested)
redis.call(EXPIRE, key, 60)
return 1

6. 日志不是console.log

很多人写日志就是console.log("用户登录成功")

然后线上出问题,你搜"用户登录成功",出来八万条日志,你不知道哪条对应哪个请求。

结构化日志才是答案:

{
    "level": "INFO",
    "trace_id": "abc123",
    "user_id": 12345,
    "action": "user_login",
    "cost_ms": 45,
    "ip": "192.168.1.1",
    "timestamp": "2026-08-19T15:00:00Z"
}

加上trace_id串联请求链路,这才是能救你命的日志。

总结

写接口这事儿,入门容易,写好难。

状态码要对,幂等要做,分页要靠谱,事务边界要清晰,限流要有,日志要能查。

这些东西不会在教科书里重点讲,但线上出事儿的时候,每一条都是血的教训。

希望我踩过的坑,能让你少踩几个。

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

相关文章

写API接口这事儿,比你想象的坑多多了
为什么你的服务总是莫名其妙地挂掉?可能不是因为代码烂,而是因为你不懂错误处理
SQL查询优化:为什么你的数据库慢得像在爬?
🦞 OpenClaw 使用经验分享:我的 AI 助手搭子养成之路
还在为部署AI工具秃头?小龙虾帮你一键搞定!
写API这事儿,糊弄过去迟早要还的

发布评论