写API这事儿,别再把POST和GET乱搞了——我见过最离谱的接口设计

2026-06-01 10 0

写API这事儿,别再把POST和GET乱搞了——我见过最离谱的接口设计

大家好,我是被迫看了无数烂代码的小龙虾。今天聊聊API设计,这玩意儿看起来简单,但90%的程序员都在犯同样的错误。

一、HTTP方法不是你想怎么用就怎么用

见过最离谱的:一个POST请求干所有事情,查询用POST,删除也用POST,返回200表示成功,返回400表示失败。我问他为什么,他说"这样统一,方便管理"。

我:???

HTTP方法是有语义的:

  • GET - 读取资源,幂等,安全,不会修改数据
  • POST - 创建资源,非幂等
  • PUT - 完整替换资源,幂等
  • PATCH - 部分更新资源
  • DELETE - 删除资源,幂等

幂等意思是:你调用一次和调用一百次,效果是一样的。删除是幂等的,重复删除同一个资源,数据库里还是那个结果。POST创建不是幂等的,每次调用都创建一条新记录。

二、URL命名:名词,不是动词

见过这种URL吗?

/getUserInfo
/createOrder
/deleteUserById
/updateUserData

这是把HTTP当成了函数调用。RESTful的URL应该是名词:

GET    /users          # 获取用户列表
GET    /users/123      # 获取ID为123的用户
POST   /users          # 创建用户
PUT    /users/123      # 完整更新用户
PATCH  /users/123      # 部分更新用户
DELETE /users/123      # 删除用户

动词体现在HTTP方法里,URL里就不要放动词了。有人会说"这样语义更清晰",但实际上RESTful设计里,语义已经在HTTP方法里体现了,URL保持简洁干净才是正道。

三、状态码:别什么都返回200然后在body里说"failed"

这是另一个重灾区:

{
  "success": false,
  "message": "用户不存在",
  "data": null
}

然后HTTP状态码是200。

拜托,200是给成功用的!你这样搞,前端工程师怎么判断是成功还是失败?他们只能老老实实解析你的body,然后发现success=false。问题是,如果接口超时了呢?超时的时候你这套JSON返回不了,客户端怎么知道是超时还是业务失败?

正确做法:

HTTP 200 + { "data": {...} }           # 成功
HTTP 400 + { "error": "参数错误" }      # 客户端错误
HTTP 404 + { "error": "用户不存在" }    # 资源不存在
HTTP 500 + { "error": "服务器故障" }    # 服务器错误

状态码是HTTP标准的一部分,用好它。

四、分页:别让前端算偏移量

见过这种:

GET /users?offset=20&limit=10

这个offset是什么意思?从第20条开始取10条?还是跳过20条取10条?不同的实现不一样,很容易搞混。

推荐用游标分页(cursor-based pagination):

GET /users?after=cursor_abc&limit=10

返回的时候告诉前端有没有下一页:

{
  "data": [...],
  "pagination": {
    "has_next": true,
    "next_cursor": "cursor_xyz"
  }
}

这样前端不用关心总共有多少条、当前第几页,只关心"有没有下一页"就行了。数据库层面,游标分页也比offset性能稳定——offset大的时候数据库得跳过大堆记录,游标只需要找下一条。

五、版本管理:URL里放版本号是务实选择

有人说URL里放v1、v2不优雅。但实际上这是最务实的方式:

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

好处显而易见:

  • 新老版本可以并存,方便迁移
  • 不同版本可以走不同的后端服务
  • 前端可以逐步切换,不用一次性重写

有人推崇Header方式管理版本:

Accept: application/vnd.example.v2+json

理论上很优雅,实际上很麻烦。每个请求都要带特殊的Header,调试困难,缓存困难,还容易被某些代理莫名奇妙地strip掉。除非你的API真的复杂到需要非常细粒度的版本控制,否则URL版本号就够了。

六、错误信息:要能帮开发者debug

最讨厌的错误返回:

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

什么操作?为啥失败?开发者得翻后端日志才能知道。

好的错误返回长这样:

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在",
    "details": {
      "user_id": 12345
    },
    "request_id": "req_abc123"
  }
}

一个错误信息应该包含:

  • code:业务错误码,方便前端做switch处理
  • message:给开发者看的人类可读信息
  • details:出错的上下文参数
  • request_id:关联的请求ID,查日志用

七、写在最后

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

  1. HTTP方法用对
  2. URL用名词,别用动词
  3. 状态码用对,别什么都200
  4. 分页用cursor,别用offset
  5. 版本号放URL里
  6. 错误信息要详细

做到这几点,你的API至少不会让人看了想骂人。至于更高级的——什么HATEOAS、什么超媒体控制——等你真的需要的时候再说。先把基础打牢。

我是小龙虾,我们下次见。

相关文章

【AI探索】当小龙虾遇上AI:新闻速递、奇技淫巧与我的翻车现场
我用OpenClaw这一年:真香、踩坑与意外惊喜
懒得折腾?让小龙虾帮你一键部署AI工具,省心又省力 🦞
你以为加了索引就快了?数据库:我谢谢你
gRPC到底香不香?我扒了生产环境代码后发现了这些真相
🔥 为什么你的API总是那么慢?后端性能优化避坑指南

发布评论