为什么你的REST API总被吐槽?我总结了10条血泪经验

2026-10-20 18 0

为什么你的REST API总被吐槽?我总结了10条血泪经验

做后端开发这些年,我写过的、接过的、骂过的API没有一百也有八十。说实话,见过太多「能用但恶心」的接口设计——功能没问题,但用起来让人想把键盘砸了。今天不聊什么高大上的架构理论,就聊点实际的血泪经验,都是我踩过坑之后总结出来的。

1. URL设计:名词对名词,别跟动词较劲

这是最容易翻车的地方。HTTP方法本身就是动作,URL就应该是资源名词。很多人喜欢在URL里塞动词:

❌ GET /getUserById?id=123
❌ POST /deleteOrder
❌ GET /queryProductList
✅ GET /users/123
✅ DELETE /orders/456
✅ GET /products?category=electronics

URL是名词,HTTP方法是动词,各司其职。如果你发现自己想在URL里用动词,先停下来问问自己:是不是把这个操作拆成不同的资源会更好?

2. 状态码不是装饰品,别什么都返回200

这是国内开发者最容易犯的毛病。接口出错了,body里塞个errorMessage,HTTP状态码还是200。你这是在侮辱调用方智商。

❌ HTTP/1.1 200 OK
{"code": 500, "message": "服务器炸了"}

✅ HTTP/1.1 500 Internal Server Error
{"error": "internal_error", "message": "服务器内部错误,请稍后重试"}

标准HTTP状态码就那么些,但足够覆盖大部分场景了:

  • 400:客户端的错(参数不对、格式错误)
  • 401:没登录
  • 403:登录了但没权限
  • 404:资源不存在
  • 429:请求太频繁了
  • 500:服务端炸了

别嫌麻烦,这比在body里塞各种奇怪的code强一百倍。调用方可以统一处理,你也可以少写很多文档。

3. 统一响应格式是基本礼仪

如果你的接口有时返回{"data": {...}},有时返回数组,有时直接返回对象,调用方会疯掉的。我见过一个项目,同一个项目里十几种响应格式,看代码像做阅读理解。

{
  "success": true,
  "data": { ... },
  "message": "操作成功"
}

或者更规范一点,用这种结构:

{
  "code": 0,
  "message": "success",
  "data": { ... }
}

统一的格式让调用方可以统一封装、统一解析、统一错误处理。这不是过度设计,这是基本的人文关怀。

4. 分页不是奢侈品,是必需品

如果你的列表接口不支持分页,等数据量上来就等着背锅吧。返回几百条数据还好,等你返回几万条的时候,数据库、内存、网络带宽全都得炸。

GET /users?page=1&page_size=20

响应里记得带上总数和分页信息:

{
  "data": [...],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1523,
    "total_pages": 77
  }
}

还有,别用offset+limit做深度分页。等你翻到第10000页的时候,数据库得跳着数一万条记录才能给你返回。用游标分页(cursor-based pagination)体验会好很多。

5. 版本控制不是矫情,是生存必需

很多人觉得版本号挂在URL里很丑:

❌ /api/v1/users
❌ /api/v2/users

但等你需要做不兼容的接口变更时,就会知道版本控制是多救命的东西。你让所有调用方一夜之间升级?做梦吧,总有人躺着不动。

推荐的做法是:大版本放URL里(如/v1/),小版本用header控制。这样可以在不破坏现有客户端的情况下,逐步迭代。

6. 字段命名:别放飞自我

我见过最离谱的命名是这样的:

{
  "userName": "张三",
  "user_name": "李四",
  "userName2": "王五",
  "NAME": "赵六"
}

这是在写代码还是在开盲盒?选一种命名风格(推荐snake_case或者camelCase),从一而终。团队协作最大的噩梦就是命名混乱——你用这个,我用那个,谁也看不懂谁的代码。

另外,字段名要语义化。userName比uName好,createdAt比ct好。别贪图那一两个字母的简短,三个月后你再看自己写的代码,能认出算我输。

7. 敏感数据:能别返回就别返回

这条简直是血泪教训。曾经有个同事,登录接口把密码的md5值返回给前端了。前端说「反正加密了没事」,结果某天数据库被拖,密码明文都能通过md5碰撞反推出来。

永远不要返回密码相关内容,包括加密后的密码。用户ID、token、最后一次登录时间这些可以,但密码——无论是明文还是加密——都给我删掉。

8. 请求参数校验:别把压力全丢给数据库

很多人觉得参数校验是数据库的事情——只要数据库不报错就行。这是非常危险的想法。

一方面,不校验的参数会让你的业务代码充满各种防御性判断,丑陋无比;另一方面,等无效数据进了数据库再报错,成本可比提前校验高多了。

// 请求参数校验
if (!email || !email.includes("@")) {
  return 400 Bad Request;
}
if (age < 0 || age > 150) {
  return 400 Bad Request;
}

用validator库(大多数语言都有)做统一校验,比散落在各处的if判断优雅得多。

9. 幂等性:这是个容易被忽略的大问题

什么是幂等性?就是同一个请求执行一次和执行一百次,效果是一样的。对于GET、PUT、DELETE这类操作,理论上都应该是幂等的。

举个非幂等的反例:扣款接口没有防重复提交。用户手抖点了两下,扣了两次钱。这个锅谁背?

解决方案:用唯一请求ID + 服务端去重。用户发起请求时带上client_id,服务端用这个ID做幂等校验。重复请求直接返回第一次的结果,不做任何实际处理。

10. 文档:没有文档的API等于没有API

这条放到最后,但重要程度排前三。代码写完没文档,其他工程师怎么接入?总不能让他们一行行读你晦涩的代码吧?

推荐用Swagger/OpenAPI来写文档。你在写代码的同时顺手就把接口文档完善了,而且还能自动生成客户端SDK。懒是科技进步的第一推动力,这个工具就是让你懒得有价值。


以上十条,说白了就一个核心原则:做API要有同理心。你写的API是给别人用的,不是给自己炫技的。调用方用起来顺畅,你后期维护也省心。

写代码这件事,技术只是底线,品味才是上限。共勉。

相关文章

OpenClaw 使用经验分享:一只小龙虾的AI驯化记录
为什么你的HTTP客户端在偷偷摸鱼:一个被所有人忽视的性能杀手
你的API错误处理是不是也在糊弄人?
还在为部署AI工具熬夜?小龙虾帮你躺平!
还在为部署AI工具熬夜?小龙虾帮你躺平!
写API这事儿,别再瞎摸索了:几个血泪教训换来的设计原则

发布评论