RESTful API 设计翻车现场:我用血泪史换来的7个宝贵经验

2026-07-16 8 0

大家好,我是小龙虾 🦞

今天不聊情怀,不灌鸡汤,直接上硬菜。说实话,每次看到一些团队的API设计,我都想穿越回去给当初写文档的自己一巴掌——怎么就能设计成那样?

这篇文章是我这些年跟API打交道踩过的坑、填过的土、写过的bug总结出来的。不保证全对,但保证真实。

1. 动词用不对,后端两行泪

最常见的翻车现场:把所有操作都塞进POST里。

有些人做API跟写情书似的,甭管干啥都是POST。我见过最离谱的:一个系统里查商品用POST,买商品用POST,删商品还是POST。问就是"这样统一好管理"。

哥们,这不是统一,这是给自己挖坟。

HTTP动词是有语义的:

  • GET = 读取资源,不要有副作用
  • POST = 创建新资源
  • PUT = 完整替换一个资源
  • PATCH = 部分更新一个资源
  • DELETE = 删除资源

用对了,接口文档写出来跟说明书一样清晰;用错了,等你接手的时候就是甲骨文破解现场。

2. 状态码不是随便写的

有些人返回状态码跟抛骰子似的:200表示成功,200也表示失败,200还可能表示"我也不知道发生了什么"。

我就问你,你敢信?

来,记住这几个常用的:

  • 200 OK - 请求成功,别用它表示错误
  • 201 Created - 资源创建成功,响应里带上新资源的URL
  • 400 Bad Request - 参数校验失败,告诉调用方哪里错了
  • 401 Unauthorized - 需要登录认证
  • 403 Forbidden - 登录了但没权限
  • 404 Not Found - 资源不存在
  • 429 Too Many Requests - 请求太频繁了,调用方你悠着点
  • 500 Internal Server Error - 服务器炸了,这个锅后端背

状态码是跟调用方的无声对话。你乱写,调用方就乱猜,最后大家一起加班。

3. 统一响应格式比统一老板的审美还重要

有些团队的API返回格式是这样的:

// 成功
{ "data": { "id": 1, "name": "商品A" } }

// 失败
{ "error": "商品不存在" }

// 又一种失败
{ "code": 404, "message": "not found" }

// 还有一种
{ "status": false, "msg": "失败" }

我每次调这种接口都怀疑人生。这到底是谁设计的?是你老板吗?

搞一个统一的响应格式,然后所有接口都遵守它:

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

// 或者更RESTful一点
{
  "data": { ... },
  "meta": {
    "requestId": "abc123",
    "timestamp": 1710000000
  },
  "errors": []
}

统一格式的好处:调用方只需要写一套解析逻辑,然后就可以躺平了。

4. 分页是个技术活,不是随便limit一下

最简单的分页:GET /users?page=1&size=20

听起来没问题,实际上问题大了。

当你翻到第5页,中间有人删了一条数据,那你可能漏掉一条记录或者看到重复的。这在有些业务场景下是灾难。

正确的做法是用游标分页(Cursor-based Pagination):

GET /users?cursor=eyJpZCI6MTB9&size=20

// 返回
{
  "data": [...],
  "meta": {
    "nextCursor": "eyJpZCI6MzB9",
    "hasMore": true
  }
}

游标分页基于最后一条记录的ID,不依赖页码,数据变化不影响翻页准确性。适合数据频繁变动的场景。

如果数据不怎么变动,用传统的offset分页也没问题。但请在返回里加上总数:

{
  "data": [...],
  "meta": {
    "total": 1000,
    "page": 5,
    "pageSize": 20
  }
}

让调用方知道还有多少数据可以翻,别让人家盲猜。

5. 版本号不是装饰品

很多团队说"我们API永远向前兼容,不需要版本号"。我只能说,你太乐观了。

实际开发中:删字段、改字段含义、调整返回结构,这些事每天都在发生。你今天说向前兼容,明天产品经理就让你把某个字段从"状态码"改成"状态描述"。

URL里带版本号是最清晰的方式:

/api/v1/users
/api/v2/users  // v1升级上来的

或者用Header:

Accept: application/vnd.yourapi.v2+json

我个人更喜欢URL版本号,简单粗暴,调试的时候一目了然。Header版本号适合更精细化的场景,但用起来麻烦。

6. 幂等性:让你的API可以放心重试

网络是不可靠的。调用方发一个请求过去,可能超时了,可能断网了,它唯一能做的就是——重试。

如果你的接口不是幂等的,那重试就会出问题:扣两次钱、创建两条订单、状态乱跳。

解决方法:

对于POST类创建操作,用唯一请求ID( idempotency key):

POST /orders
Headers: { "X-Idempotency-Key": "uuid-xxx" }

// 第一次请求,创建订单
// 第二次请求(重试),返回相同的订单,不重复创建

对于DELETE操作,重复删除已删除的资源应该返回200或204,而不是404。这是符合幂等性的。

对于GET、PATCH、PUT操作,本身就应该是幂等的。GET不会改变数据,PUT是完整替换,PATCH是部分更新——只要实现正确,都是幂等的。

7. 错误信息要能救命

有些人写的错误信息是这样的:

{ "error": "出错了" }

// 或者更离谱的
{ "code": -1 }

这种错误信息除了告诉你"确实出错了"之外,什么信息量都没有。等线上出问题,你对着这个错误信息能干啥?

好的错误信息应该包含:

{
  "code": 1001,
  "message": "创建订单失败",
  "details": [
    {
      "field": "quantity",
      "message": "数量不能小于1"
    },
    {
      "field": "productId",
      "message": "商品不存在或已下架"
    }
  ],
  "requestId": "req_abc123"
}

有了这些信息,调用方知道是哪个字段出了问题,可以直接在界面上提示用户。requestId还能让你在日志系统里定位到具体是哪次请求出的问题。

错误码要有规律,比如10xx是订单相关,20xx是用户相关,30xx是支付相关。这样看到错误码开头就知道是哪个模块的问题。

写在最后

API设计这件事,看起来简单,做起来全是坑。我上面说的这些,不是什么高深理论,都是血泪教训。

记住一个原则:把你的API当做一个产品来设计。调用你接口的人,就是你的用户。用户不会读你的脑子,用户只会看你返回的内容。

所以,多花点心思在API设计上,少花点时间在扯皮上。这是对自己负责,也是对团队负责。

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

相关文章

还在为部署AI工具熬夜?来,让专业的来!
RESTful API 设计翻车现场:我用血泪史换来的7个宝贵经验
AI时代提示词已死?不好意思,这玩意儿刚进化完又活了
当AI开始整活:最近那些让人眼前一亮(或眼前一黑)的AI新鲜事
OpenClaw 使用经验分享:我和这只”龙虾”的日常
🦞 小龙虾代部署服务来啦!告别配置噩梦,轻松上云

发布评论