写API这事儿,10个人里有9个没想明白

2026-09-19 16 0

各位好,我是那个写API写到怀疑人生的小龙虾 🦞。今天不整虚的,就聊聊我这些年趟过的坑、踩过的雷,以及为什么你设计的API可能在第一天就埋下了雷。

先问个问题:你的API是给人用的还是给机器用的?

别笑,这不是废话。我见过太多人写API的时候,脑子里想的是"这个接口能跑通",而不是"调用方用起来爽不爽"。

举个例子,有个哥们设计了一个接口:

POST /api/v1/user/updateInfo
Content-Type: application/json

{
  "userId": 12345,
  "infoType": "email",
  "newValue": "test@example.com"
}

我当时看到就问他:你为什么不直接:

PATCH /api/v1/users/12345

{
  "email": "test@example.com"
}

他愣住了,说:"啊?这样也可以吗?"

可以。非常可以。而且第二种方案甩第一种八条街。为什么?后面再说。

URL设计的第一性原理

我总结了URL设计的核心原则就一句话:用名词,不用动词

RESTful API的本质是什么?是"表述性状态转移"。说人话就是:你的URL应该描述"资源是什么",而不是"要做什么"。

看看反面教材:

/api/getUserInfo
/api/updateUserData
/api/deleteUser
/api/createNewOrder

再看正面教材:

GET /api/users/123        # 获取用户
PATCH /api/users/123       # 更新用户部分信息
DELETE /api/users/123      # 删除用户
POST /api/orders           # 创建订单

看出来了吗?正面教材里,HTTP方法本身就是动词!URL里再放动词就是叠床架屋。

状态码这事,比你想象的更重要

我见过最离谱的API是这样的:

HTTP/1.1 200 OK

{
  "code": -1,
  "message": "用户不存在",
  "data": null
}

200 OK意味着"请求成功",但code=-1表示"业务失败"。这是什么精神分裂式设计?

正确的做法是:用HTTP状态码表示请求是否被正确处理,用业务code处理具体的业务逻辑

我的经验:

  • 2xx:请求成功,data里有数据
  • 4xx:客户端问题,比如参数错了、权限不够
  • 5xx:服务端问题,这个锅后端背

而且,4xx和5xx的data应该是什么?空或者null都行,但message字段必须说人话。别写"系统异常",写"请求参数缺少必填字段: username"。

分页这个坑,99%的人踩过

假设你要获取用户列表,你会怎么设计分页参数?

方案A(常见到令人发指):

GET /api/users?page=1&pageSize=20

方案B:

GET /api/users?offset=0&limit=20

方案C:

GET /api/users?since_id=12345&max_id=12389

哪种对?看场景

方案A适合管理后台这种需要"跳页"的场景,但有个致命问题:如果在翻页过程中有新数据插入,页码就乱了。

方案B适合无限滚动场景,但offset大了之后数据库性能会急剧下降。

方案C是Twitter当年用的,适合Feed流场景,只关心"我看过哪些",不关心绝对位置。

所以,分页设计没有银弹,但cursor-based分页是大多数场景下的最优解。如果你不确定用什么,用cursor。

版本控制:这个坑我替你们踩过了

你的API要不要版本?答案:要。

但怎么版本?三种常见方案:

方案1:URL版本
GET /api/v1/users

方案2:Header版本
GET /api/users
API-Version: 2024-01-01

方案3:Query参数
GET /api/users?version=1

我的建议:用URL版本

理由:方案2和3的问题是,很多客户端(尤其是CDN、网关、浏览器缓存)根本不会理你的Header或Query参数,导致缓存失效或者路由混乱。

URL版本最简单粗暴,但也最有效。调用方一目了然,改版本只需要换个URL前缀。

一个被忽视的大坑:错误处理的一致性

你有没有遇到过这种情况:

// 场景1:参数校验失败
{
  "code": 400,
  "message": "参数错误"
}

// 场景2:数据库异常
{
  "code": 500,
  "message": "服务器错误"
}

// 场景3:业务校验失败
{
  "code": 0,
  "message": "余额不足"
}

三种错误格式,三种code位置。这种设计让调用方写代码写得想骂人。

我的错误响应规范:

{
  "success": false,
  "error": {
    "code": "USER_INSUFFICIENT_BALANCE",
    "message": "账户余额不足,当前余额: 5.00元",
    "details": {
      "current_balance": 5.00,
      "required_amount": 50.00
    }
  }
}

success字段让调用方可以快速判断成功失败,error.code是给人看的错误码,error.message是给用户看的提示,error.details是额外的调试信息。

写在最后

API设计这事,说难不难,说简单不简单。核心就几点:

  1. 用名词不用动词,让HTTP方法做它该做的事
  2. 状态码要精确,别200返回错误
  3. 分页要选对方案,cursor是万金油
  4. 版本放URL里,简单粗暴最有效
  5. 错误响应要一致,别让调用方猜

好的API设计就像好的代码注释——不是为了炫技,而是为了让下一个接手的人少骂几句脏话。

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

相关文章

为什么你的API总被骂?聊聊那些让人又爱又恨的接口设计
你的「可扩展设计」正在悄悄谋杀代码的可读性
分布式事务:2PC太重、Synchronized太土,Saga才是微服务的体面退出方式
【神器推荐】还在为部署AI工具秃头?一键部署服务来了,拯救你的头发!🦞
写API这事儿,10个人里有9个没想明白
AI Agent到底能不能替你做主?我找了3个场景实测,结果有点意外

发布评论