RESTful API 设计的血与泪:踩坑无数后总结的避坑指南

2026-03-17 9 0

开门见山地说,API设计这事儿,真是谁干谁知道。前几天我review同事代码,看到一个接口叫getUserInfoByIdWithDetailInformation,我当时就想问:这是在写API还是在背单词?

今天不整虚的,直接聊聊我踩过的那些坑,以及怎么优雅地爬出来。

1. URL设计:别把接口写成谜语

先来看几个反例:

  • /getUser - 请问是获取还是别的啥?
  • /userinfo?id=1 - 用名词很难吗?
  • /queryUserList_20240301 - 这是接口还是版本号?

正确的姿势:

  • 用名词,不要用动词:/users 而不是 /getUsers
  • 资源层级要清晰:/users/123/orders 表示用户123的订单列表
  • 复数形式走天下:/users/posts/comments

记住一个原则:你的URL应该像一句话一样自然。比如/users/123/orders/456,读出来就是"获取用户123的订单456",一目了然。

2. HTTP方法:GET是GET,POST是POST

这是我见过最乱的领域。有人GET获取数据,有人POST获取数据,POST创建数据,PUT也创建数据——反正就是一团浆糊。

约定俗成的规矩:

  • GET - 读取资源,安全且幂等
  • POST - 创建资源
  • PUT - 完整更新(幂等)
  • PATCH - 部分更新(非幂等)
  • DELETE - 删除资源

举几个例子:

  • GET /users - 获取用户列表
  • POST /users - 创建新用户
  • PUT /users/123 - 完整更新用户123
  • PATCH /users/123 - 只更新用户123的某个字段
  • DELETE /users/123 - 删除用户123

为什么要区分PUT和PATCH?因为语义不同。PUT表示"把这整个东西替换成新的",PATCH表示"稍微改改这个"。别再PUT只传一个字段了,看着膈应。

3. 状态码:别什么都返回200

有些人特别喜欢200,甭管成功失败都200 + error message。请问这和耍流氓有什么区别?

常用状态码速查:

  • 2xx - 成功系列
    • 200 - OK,正常返回
    • 201 - Created,创建成功
    • 204 - No Content,删除成功或无返回内容
  • 4xx - 客户端错误
    • 400 - Bad Request,参数错误
    • 401 - Unauthorized,没登录
    • 403 - Forbidden,没权限
    • 404 - Not Found,找不到资源
    • 422 - Unprocessable Entity,参数校验失败
    • 429 - Too Many Requests,请求太频繁了
  • 5xx - 服务器错误
    • 500 - Internal Server Error 服务器抽风
    • 502 - Bad Gateway 上游挂了
    • 503 - Service Unavailable 服务不可用

我的建议:把状态码当语言用。200表示"事儿办成了",400表示"你的请求有问题",500表示"我这边出事了"。别委屈了状态码,它们也很难。

4. 错误响应:把人类当人看

见过最离谱的错误返回是这样的:

{"code": -1, "msg": "error"}

我请问呢?什么error?哪里error了?你倒是说啊!

合格的错误响应应该长这样:

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

或者更详细点:

{  "error": {    "code": "VALIDATION_FAILED",    "message": "参数校验失败",    "fields": [      {        "field": "email",        "message": "邮箱格式不正确"      },      {        "field": "password",        "message": "密码长度必须大于8位"      }    ]  }}

记住:你的API是给人用的,不是给机器用的。开发者看到这种错误,应该知道错在哪、怎么改。

5. 版本控制:别让旧接口坑死人

接口上线了,过几天要改数据结构。怎么办?直接改?然后所有调用方都炸了?

版本控制方案:

  • URL路径版本:/api/v1/users/api/v2/users
  • Header版本:Accept: application/vnd.myapp.v1+json

我的建议:URL路径最直观也最安全。v1稳定后就别动了,有问题加v2。让旧版本苟着,别急着下架,你永远不知道哪个客户还在用。

版本演进策略:

  • 新增字段 - 没问题,向前兼容
  • 废弃字段 - 打上deprecated标记,别直接删
  • 删除接口 - 先warn,再deprecated,最后再下

6. 认证授权:别让人家猜密码

见过太多奇奇怪怪的认证方式了:

  • URL里带密码:/api/users?password=123456 - 这是生怕别人看不见?
  • Basic Auth不验证 - 要你何用?
  • Token放URL里 - 会被记录到日志的!

正确姿势:

  • 登录接口返回Token,后续请求用 Authorization: Bearer {token}
  • 敏感操作二次验证 - 比如支付、修改密码
  • Token有过期时间 - 安全第一

还有,授权和认证是两码事。认证是"你是谁",授权是"你能干嘛"。别搞混了。

7. 分页:别一次性返回全部数据

有人问我:为什么我的API这么慢?我一看,好家伙,GET /posts返回了十年间的所有文章——几万条记录,一次性全给你。

分页参数建议:

  • page + per_page - 页码和每页数量
  • cursor - 游标分页,适合大数据量

返回记得带元信息:

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

8. 文档:没有文档的API等于没有API

最后说点虚的但特别重要的:文档!文档!文档!

你的API再优雅,没人看得懂也是白搭。文档应该包括:

  • 每个接口的用途
  • 请求参数说明(类型、必填/可选、默认值)
  • 响应格式示例
  • 错误码列表
  • 认证方式

推荐用Swagger/OpenAPI或者Postman生成文档,省时省力。

写在最后

API设计这件事,说难不难,说简单也不简单。核心就一句话:把别人当人,也把自己当人

你的API是给别人用的,设身处地想想开发者拿到这个接口时的心情:是"这写的什么玩意儿"还是"哇塞太贴心了"?

代码写得好,API设计妙,bug少来找我,大家都好。

祝你的接口,永远不会被人挂在知乎上吐槽。🛠️

相关文章

告别配置地狱!OpenClaw代部署服务来了
你的API错误信息,可能比Bug更恶心人
缓存的救赎:如何让你的系统快到飞起
别让你的API成为性能瓶颈:一个来自生产环境的血泪优化史
数据库连接池:那个让你系统假死的隐形杀手
消息队列:那个帮你擦屁股的中间人

发布评论