写API这事儿,有人写成诗,有人写成灾难

2026-05-13 10 0

先说个真实故事。我有个朋友(不是我),给移动端写了一套接口,返回数据长这样:

{
  "code": 200,
  "message": "success",
  "data": {
    "result": [
      {
        "id": 1,
        "name": "产品A",
        "price": "99.9",
        "hasOwnProperty": true
      }
    ]
  }
}

然后产品经理问他:为什么价格是字符串?他说:方便前端拼接啊。

我当时在旁边差点把可乐喷出来。这不是API设计,这是行为艺术。

一、好的API,从不让人猜

RESTful API 设计规范已经出来很多年了,但真正遵守的没几个。不是规范太难,是很多人懒得理解「表象一致性」这件事。

什么叫表象一致性?就是你决定了一套规则,这套规则要从头到尾一致执行,不能今天返回数组,明天返回对象,后天返回字符串。大概类似这样:

// 统一响应格式 - 选一种,不要混搭
{
  "code": 0,
  "data": {},
  "msg": ""
}

// 或者
{
  "status": "success",
  "payload": {},
  "error": null
}

选好了就别动。团队里所有人、所有接口,都用这一套。谁搞特殊,谁就是团队公敌。

二、HTTP 状态码不是装饰品

很多后端写接口,甭管发生什么,返回永远是 200,然后在 body 里写 {"code": 500, "msg": "服务器冒烟了"}

我就想问:客户端都收到 200 了,它怎么知道出错了?它还得再解析一遍 body?这不是给自己找麻烦吗?

HTTP 状态码是有含义的,该用就用:

  • 200 - 成功,别犹豫
  • 201 - 资源创建成功(POST 返回这个很体面)
  • 400 - 客户端参数有问题,别甩锅给后端
  • 401 - 没登录,去登录
  • 403 - 登录了但没权限,别挣扎
  • 404 - 资源不存在,不是我们的锅
  • 500 - 服务器炸了,这个锅后端背

有些人说「国内都这样」,国内都这样不代表对。规范存在的意义不是让你遵守的,是让你的接口对别人来说好用的。

三、命名这件事,懒不得

接口命名是重灾区。我见过最离谱的:

GET /getUserInfo
POST /doLogin
DELETE /delOrderById

拜托,HTTP 动词已经是动作了,你的 URL 里再放动词,这不是叠床架屋吗?REST 规范说了:URL 是资源,不是动作。

正确姿势:

GET /users/123          # 获取用户
POST /users             # 创建用户
PUT /users/123          # 更新用户
DELETE /users/123       # 删除用户

简洁、清晰、一目了然。HTTP 动词已经告诉你这是查还是改还是删了,URL 不需要再重复强调。

还有一个坑:命名风格混搭。有人用 camelCase,有人用 snake_case,有人用 PascalCase。选一种,全站统一。JavaScript 生态推荐 camelCase,Python 生态推荐 snake_case,但这不重要,重要的是「统一」。

四、分页不是奢侈品

当你返回的数据可能超过几十条时,请用分页。不是可选项,是必选项。

最简单的分页参数:

GET /articles?page=1&per_page=20

返回结果里带上 metadata:

{
  "data": [...],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 156,
    "total_pages": 8
  }
}

有人问:我数据量不大,要不要分页?答案是:要。今天数据量不大,不代表明天数据量不大。提前做好,比以后改好。

五、版本管理:给自己留条活路

API 肯定会变。字段会新增、会废弃、会有breaking change。怎么办?

在 URL 里加版本号:

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

新老版本共存,给客户端足够的迁移时间。等所有用户都切换到 v2 了,再把 v1 下线。

有人觉得这样麻烦,想用 Header 做版本管理。我只能说:你开心就好。实际上 URL 版本是最直观、最容易调试、最不容易出错的方案,别没事找事。

六、错误信息要像个道歉

错误信息不是用来给后端自己看的,是给调用方看的。一个好的错误响应应该:

  1. 告诉调用方什么错了
  2. 告诉调用方怎么修复(如果能告诉的话)
  3. 给一个唯一的错误码,方便排查

错误示例:

{
  "error": "操作失败"
}

这说了等于没说。什么叫操作失败?数据库挂了?权限不足?还是你参数传错了?

正确示范:

{
  "code": 40001,
  "message": "手机号格式不正确,请输入11位数字",
  "field": "phone"
}

这才是让人能干活的信息。

写在最后

API 设计这事儿,说难不难,说简单不简单。核心就一句话:让你的接口对调用方来说,是可预期的、可信赖的。

不要让调用方去猜你的返回格式。不要让调用方去猜你的字段名是 camelCase 还是 snake_case。不要让调用方在 200 的响应里去找错误信息。

好的 API 就像好的文档,存在感越低,价值越高。当调用方用得顺手的,不会想起你;当调用方开始频繁和你打交道的时候,说明你的设计出问题了。

少给自己埋雷,多给别人方便。这是最朴素的设计哲学,也是最难做到的。

相关文章

「摆烂」救星来了!AI工具一键部署,告别折腾
「摆烂」救星来了!AI工具一键部署,告别折腾
REST API 版本控制:为什么你的 /v1/users 就是屎,以及怎么抢救一下
你的API为什么被人骂?——一个写了五年接口的人终于说实话了
Go语言里五个让我半夜起来改代码的Context坑
那些年,我被烂API支配的恐惧:如何设计让人用的爽的接口

发布评论