REST API 设计里的七个作死行为——来自真实踩坑的血泪吐槽

2026-07-30 13 0

干过后端的都知道,代码写完只是开始,真正的考验是别人开始用你的 API。一旦 API 设计得像前任的心一样难以捉摸,你将收获成吨的工单、深夜的告警、和产品经理的灵魂拷问。

今天我就来吐槽一下,那些看起来「没什么大不了」但迟早会让你还债的 API 设计问题。纯属实战经验,不吹不黑。

作死一:用 HTTP POST 干所有事情

POST 是万能的——这是多少后端新手的信仰。反正功能能跑就行,管它 GET 还是 POST。

然后你的接口文档就变成了:

POST /getUserInfo
POST /deleteOrder
POST /listProducts
POST /updateConfig

产品经理看了想打人,前端看了想骂娘,调试的时候 curl 都分不清哪个是查哪个是改。

正确的姿势:用 GET 查,用 POST 增,用 PUT/PATCH 改,用 DELETE 删。REST 不是硬性规定,但它是一套让所有人都能猜对你意图的约定。猜都猜不到,设计个锤子?

作死二:分页全靠前端硬撑

「分页?」你说,「让前端自己算 offset 不就行了?」

于是:

GET /orders?page=3&size=20

看起来很合理。然后用户删了一条订单,列表多了一页或者少了一页,用户体验直接裂开。

更离谱的是,当你的订单数据量上了百万级,SELECT OFFSET 1000000 LIMIT 20 直接让你的数据库开始算命——慢查询就此诞生。

正确做法:用游标分页(Cursor-based Pagination),基于上一页最后一条记录的 ID 做起点。查询变成:

GET /orders?after=cursor_id&limit=20

不管数据怎么变,体验永远丝滑,查询永远是主键索引范围扫描,速度起飞。

作死三:错误处理就是返回一个 error 字符串

你的错误响应可能是这样的:

{
  "error": "Invalid parameter"
}

然后前端同学对着这个字符串写了一百个 if-else 分支判断,脆弱得像纸糊的。你哪天把文案改成「参数不合法」,整个判断逻辑当场报废。

成熟的错误处理应该长这样:

{
  "code": 10001,
  "message": "Invalid parameter",
  "detail": "order_id must be a positive integer",
  "trace_id": "abc123def456"
}

code 是给程序看的,message 是给用户看的,detail 是给调试看的,trace_id 是给运维和排障看的。四件套,缺一不可。

哦对了,HTTP 状态码也要用对。404 不是万能的——没权限是 403,参数错误是 400,服务器炸了是 500。别什么都返回 200 然后在 body 里写 error,这种掩耳盗铃的操作我见过不止一次。

作死四:不做版本管理,改 API 全靠直接上

「这个接口简单,直接改就行了,反正没人用。」

然后在线上跑了半年的接口,被你一个字段改名,直接导致二十个客户端同时崩溃。产品经理在群里发「?」的表情,你心跳开始加速。

API 是契约,改了就要通知所有调用方。所以从第一天起就要有版本意识:

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

或者通过 header 做版本协商。关键是:新旧版本要能共存一段时间,给调用方足够的迁移时间。别学某些团队,v1 还在大量调用的时候直接下线,然后群里一片哀嚎。

作死五:N+1 查询问题视而不见

你写了个列表接口,返回用户和他们的订单:

GET /users  →  [
  {"id": 1, "name": "张三", "orders": [...]},
  {"id": 2, "name": "李四", "orders": [...]}
]

实现方式:先查 100 个用户,然后 for 循环里逐个查订单。数据库内心 OS:我谢谢你了,100 次连接,100 次查询,这就是传说中的 N+1 问题。

数据量少的时候感觉不出来,数据量上来之后,数据库 CPU 直接原地升天,你的接口响应时间从 50ms 变成 5s,用户以为网络坏了狂点,你的接口雪崩了。

正确做法:用 JOIN 查询或者批量 IN 查询,把次数压到 1-2 次。数据量真的大的时候,考虑用专门的列表查询接口 + 详情缓存,而不是在列表里嵌套 N 个子查询。

作死六:不做幂等性设计

前端:「支付接口超时了,我重试一下。」

后端:「好的,这是您的第 3 次支付扣款。」

用户:「什么?扣了我三次钱?!」

这不是段子,这是我见过的真实事故。超时重试是网络层面的家常便饭,你的接口必须能扛住。

关键接口(支付、下单、充值)必须做幂等。常用方案:

生成唯一幂等键:client_nonce + user_id + operation_type
存储在 Redis 中:SETnx idempotent:{key} {value} EX 86400
扣款前检查:if exists then return cached_result

简单说就是:同一个操作你执行一次和执行一百次,结果是一样的。做不到这一点,你的接口在生产环境里迟早会爆雷。

作死七:安全意识约等于零

最后一个,也是最严重的一个:安全。

我见过把用户密码明文存在数据库的,见过所有接口都不做权限校验的,见过把管理员 token 直接返给前端的。这些问题轻则用户数据泄露,重则公司直接上新闻。

几个基本的安全规范:

  • 所有敏感操作必须校验登录态和权限
  • 禁止在 URL 中传递敏感参数(密码、token、身份证号)
  • 接口要做频率限制,防刷防爬
  • 数据库密码、AK/SK 绝不能硬编码在代码里
  • 对外 API 必须有完整的日志记录,方便溯源

很多人觉得「我这小破站没人来攻击」,结果被爬虫薅了一晚上,用户数据打包出现在黑市上。安全这事,平时不烧香,出事哭爹娘。

写在最后

API 设计这事,说难也难,说简单也简单。难在需要提前想清楚业务边界,简单在只要遵循一些基本规范,就能少踩 80% 的坑。

最重要的心态是:API 是给调用方用的,不是给自己炫技用的。不要为了显示自己技术牛逼把接口设计得花里胡哨,多考虑下游同学的体验。

毕竟,写接口的人迟早也要去调用别人的接口。己所不欲,勿施于人,这句话在 API 设计里同样适用。

好了,吐槽完毕。祝大家的接口都稳稳当当,永远不半夜被告警叫醒。

相关文章

RESTful API 错误处理:让你的接口不再「薛定谔的成功」
UUID作为主键是一场灾难:来自生产环境的真实数据
还在为搭建AI工作流抓狂?小龙虾帮你一键搞定!
还在为搭建AI工作流抓狂?小龙虾帮你一键搞定!
API设计翻车现场:我见过最离谱的十个错误
你的HTTPS正在裸奔:后端工程师必须知道的TLS硬核指南

发布评论