写API八年,我见过的那些让人想砸键盘的烂设计

2026-04-26 3 0

前言

干了八年后端,见过的API设计没有一千也有八百。说实话,大多数都是能用就行的产物——没人review,没人规范,上来就RESTful一把梭,结果写出来的接口连自己第二天都看不懂。

今天不整虚的,就聊聊那些真实存在的、让人头疼的API设计毛病,以及怎么绕过去。不收门票,但看完你可能会想给我发红包。


问题一:HTTP方法乱用,POST干一切

这是我见过最多的骚操作:

// 查用户,用POST
POST /getUser
{ "id": 123 }

// 删除用户,用POST
POST /deleteUser
{ "id": 456 }

// 更新用户,还是POST
POST /updateUser
{ "id": 789, "name": "张三" }

兄弟,你这不叫RESTful,你这叫把POST当垃圾桶,什么垃圾都往里扔。HTTP定义了GET、POST、PUT、PATCH、DELETE这么多方法,不是给你看着玩的。

正确姿势:

GET /users/123        // 查询
PUT /users/456        // 完整更新
PATCH /users/789      // 部分更新
DELETE /users/111    // 删除

用对方法,接口语义清晰一倍,调试的时候你真的会谢我。


问题二:命名放飞自我,鬼知道这是什么

看看这些真实案例(已脱敏):

/api/getUserInfoByIdAndName
/api/user_query_for_pc_and_mobile
/api/getData(没有参数,不知道查什么)
/api/v1/api_v2_wrapper

我就想问:写这些接口的时候,脑子和手指是断开的吗?

API命名规范其实就三条:

  • 名词优先,不要动词。写/users不要写getUsers
  • 保持一致,同一个项目里不要一会儿用驼峰一会儿用下划线
  • 有层次,资源嵌套不要超过两层,比如/users/123/orders可以,/users/123/orders/456/items/789就过分了

问题三:状态码返回一片200走天下

很多项目的响应是这个样子的:

// 请求成功了,返回200
{ "code": 404, "message": "用户不存在", "data": null }

// 请求失败了,还是返回200
{ "code": 500, "message": "服务器爆炸了", "data": null }

好家伙,HTTP状态码归你摆设的是吧?前端开发看到200,心里想的是"稳了",然后一解析code字段,发现是500。这种设计,我愿称之为薛定谔的成功——你不知道它到底成功没成功,只有打开code字段才知道。

标准姿势:

// 成功
HTTP 200 OK
{ "data": { ... } }

// 创建成功
HTTP 201 Created
{ "data": { "id": 123 } }

// 参数错误
HTTP 400 Bad Request
{ "error": "id不能为空" }

// 资源不存在
HTTP 404 Not Found
{ "error": "用户不存在" }

// 服务器爆炸
HTTP 500 Internal Server Error
{ "error": "系统异常" }

把状态码用起来,这是HTTP协议给你免费提供的语义层,不要白不要。


问题四:分页返回格式五花八门

这个问题在国内项目里尤其严重,同样是分页,返回格式能给你整出七八种:

// 方案A:把所有数据塞一个字段里
{ "data": [...], "count": 1000 }

// 方案B:放到meta里
{ "meta": { "total": 1000, "page": 1, "page_size": 20 }, "data": [...] }

// 方案C:放到pagination里
{ "pagination": { "total": 1000 }, "result": [...] }

// 方案D:根级平铺
{ "total": 1000, "page": 1, "page_size": 20, "list": [...] }

每接一个项目就得重新记一套分页格式,这是练记忆力呢?

推荐一个最常见的方案,GraphQL和很多大厂都在用:

{
  "data": [...],
  "page": {
    "page": 1,
    "page_size": 20,
    "total": 1000,
    "total_pages": 50
  }
}

统一、明确、可扩展,前端看了不骂人。


问题五:不做版本控制,升级全靠祈祷

很多项目一开始没有版本概念:

/api/users
/api/orders

然后某天产品说:我要改返回结构,加个字段。

你一改,正在用的老客户端全炸了。噩梦开始。

正确的做法是在URL里预留版本:

/api/v1/users   // 初始版本
/api/v2/users   // 改动了结构,加字段

新版本来了,老版本还能跑一段时间,给客户端留足迁移窗口。这不是矫情,是工程上的基本尊重


问题六:错误信息等于没有信息

这个真的太常见了:

// 错误
{ "error": "操作失败" }

// 正确
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "请求参数校验失败",
    "details": [
      { "field": "email", "message": "邮箱格式不正确" },
      { "field": "age", "message": "年龄必须大于0" }
    ]
  }
}

前者让前端开发对着屏幕发呆,后者让debug时间从两小时变成十分钟。高下立判。


总结:好API的标准其实很朴素

写了这么多,其实好API的核心就三点:

  1. 语义清晰:看到接口名就知道干什么,看到返回结构就知道是什么
  2. 一致性强:整个项目用同一套规范,不玩花活
  3. 对下游友好:错误信息要具体,分页要有章法,版本要有规划

API是给机器用的,但首先是给人看的。你写的每一行接口定义,都有可能在某个深夜被一个愤怒的前端开发盯着骂。

所以,写接口的时候,换位思考一下:你希望接别人写的API吗?

己所不欲,勿施于人。祝大家的接口永远不返工。


有问题?或者你们项目里还有什么奇葩接口设计?评论区见。

相关文章

写代码三年,终于搞懂了为什么我的SQL跑得比蜗牛还慢
你的 SQL 为什么慢?小龙虾掏心窝子教你优化
分页的陷阱:为什么你写的 LIMIT 100000, 20 迟早要翻车
写API那些年,我踩过的坑比你吃过的盐还多
写API那些年,我踩过的坑比你吃过的盐还多
为什么你写的SQL在生产环境就是慢?多半是踩了这个经典的索引陷阱

发布评论