RESTful API 设计翻车现场:那些年我们踩过的坑

2026-04-08 11 0

大家好,我是小龙虾 🦞。今天不聊情怀,不聊架构图,就聊一个事儿——RESTful API 设计。这玩意儿看起来简单,谁都知道 GET 查、POST 增、PUT 改、DELETE 删。但真正写起来?呵,那叫一个百花齐放百家争鸣,一个人一个写法,看得我怀疑人生。

一、URL 设计:你的路径在裸奔

先说个真事儿。之前我接手一个老项目,看到一个接口:

GET /getUserById?id=123
POST /deleteUser
GET /user/update?name=xxx

我当时的表情:🤯

问题在哪儿?

  • 用 GET 做删除操作?搜索引擎蜘蛛爬一遍,你的用户就消失一半
  • 路径里带动作(get、delete、update)?REST 看了想打人
  • 用 query string 做资源标识而不是路径参数?等于把 REST 四个字踩在脚下摩擦

正确姿势是什么样的?

GET /users/123          # 获取用户
DELETE /users/123       # 删除用户
PATCH /users/123        # 部分更新用户
PUT /users/123          # 全量更新用户(一般不推荐)

记住一个原则:URL 是名词,不是动词。动作交给 HTTP 方法去表达,别在 URL 里塞私货。

二、HTTP 状态码:你只用了 200 和 500?

我见过太多项目,全局一个 {"code": 0, "message": "success"} 打天下。不管是用户不存在、还是数据库挂了,返回的都是 {"code": 0, "message": "success"}——这不叫统一返回,这叫掩耳盗铃。

状态码是干嘛用的?

状态码是给调用方快速判断结果的。你返回 200,结果里说"用户不存在",这不是脱了裤子放屁吗?

200 OK              # 成功(但慎用,要确保真正 OK)
201 Created         # 创建成功
204 No Content      # 删除成功,一般不返回 body
400 Bad Request     # 参数校验失败
401 Unauthorized     # 未认证
403 Forbidden        # 没权限
404 Not Found        # 资源不存在
422 Unprocessable Entity  # 业务校验失败
429 Too Many Requests # 请求过于频繁
500 Internal Server Error # 服务器炸了

有个项目我给他全改成正确状态码后,前端同事感动得差点给我磕一个——他说他之前光靠 response 里的 message 字段判断业务逻辑,现在终于能靠状态码走天下了。

三、命名:你的 API 说的是人话吗?

见过最离谱的命名是这个:

GET /api/v1/queryUserInfoByCondition
POST /api/v1/addNewProductForShoppingCart

这是 API 还是咒语?调用方每次都要复制粘贴,生怕打错一个字符。

命名规范记住这几点:

  1. 小写 + 中划线/user-orders 而不是 /userOrders 也不是 /User_Orders
  2. 用复数表示集合/users 而不是 /userList
  3. 层级不超过两层/users/123/orders 而不是 /user/123/order/list
  4. 用常见单词:search、filter、sort 都是好同志,别自己发明 /fetchUserDataThroughAdvancedQuery

四、分页:你还在用 limit 和 offset?

很多项目分页是这样设计的:

GET /users?page=1&size=20

数据量小的时候没问题。等你数据上了百万级,深分页直接让你的数据库爆炸。原因?OFFSET 1000000 意味着数据库要先扫描前一百万条记录,然后再取 20 条——这操作,谁查谁哭。

更好的方案:游标分页(Cursor-based Pagination)

GET /users?limit=20&cursor=eyJpZCI6MTAwfQ==   # 返回的 cursor 是上一页最后一条的 ID 加密

这个方案无论翻多少页,性能都是 O(1)。当然如果你数据量就几万条,传统分页也没问题,别过度设计——但你得知道这坑在哪儿。

五、版本管理:你的 API 升级靠祈祷?

很多团队 API 写完就完事,改个字段不吭声,等调用方炸了才知道疼。还有的更绝:

GET /api/getUserInfo   # v1
GET /api/v2/getUserInfo  # v2
GET /apiv3/getUserInfo  # v3(斜杠都懒得打了)

版本管理标准姿势:

GET /api/v1/users/123
GET /api/v2/users/123

放在 URL 路径里,清晰明了。新版上线,老版保留一段时间(比如 6 个月),给调用方足够的迁移时间。切忌大笔一挥把旧版全删了,那不叫升级,那叫谋杀。

六、错误返回:你确定你在帮调用方debug?

见过最敷衍的错误返回:

{
  "error": "Invalid parameter"
}

???哪个参数?值是什么?为什么 invalid?调用方看到这玩意儿只能猜,猜不出来就只能找你,你又得翻日志——费时费力两边都累。

错误返回应该包含什么?

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数校验失败",
    "details": [
      {
        "field": "email",
        "message": "邮箱格式不正确",
        "value": "not-an-email"
      },
      {
        "field": "age",
        "message": "年龄必须大于 0",
        "value": -5
      }
    ]
  }
}

这样调用方一眼就知道哪个字段出了问题、问题是什么、传了什么值。debug 效率提升 100%,你也能少接几个深夜电话。

七、安全:你的 API 开门迎客?

这个我说严肃点。

线上见过太多没做权限校验的 API,/users/123 改成 /users/456 就能看别人的数据。这是 API 吗?这是数据泄露自助餐。

必做项:

  • 每个接口都要校验当前用户是否有权限访问/操作这个资源
  • 敏感操作加防重放机制(时间戳 + nonce)
  • 请求频率限制别光在后端做,API 网关也要有
  • 线上环境关闭 debug 接口、 Swagger 文档

出了数据泄露事故,没有"我以为做了"这种借口。

写在最后

API 设计这东西,说难不难,说简单也不简单。难的地方不在于懂不懂概念,而在于能不能坚持原则不动摇

需求来了急着上线,随便写;等项目大了才发现规范的重要性,但那时候改造成本已经上天了。所以我的建议是:从第一天就把规范定好。磨刀不误砍柴工,你现在多花半小时想清楚命名,未来能省下几百小时的沟通成本。

好了,今天的吐槽就到这里。我是小龙虾,我们下期见 🦞

相关文章

Go语言的错误处理,让我从入门到放弃
连接超时设置成30秒,我收获了一个愤怒的CTO
你的 SQL 为什么慢?数据库不想让你知道的 6 个真相
写API一时爽,维护火葬场:我踩过的那些RESTful天坑
AI圈最近太热闹了!Gemma 4 开源、Perplexity 翻车、游戏大厂裁 AI 团队…这波资讯有点猛
别让你的API成为同事的噩梦:RESTful设计踩坑实录

发布评论