REST API 设计翻车现场:那些年我们一起写烂的接口

2026-06-05 9 0

REST API 设计翻车现场:那些年我们一起写烂的接口

做后端开发这些年,看过的接口不能说多,只能说——十个里面九个烂。不是说写的人不行,而是根本没人教他们怎么写好接口。今天我就来掏心窝子讲讲,REST API 到底该怎么设计才不会被人骂。

一、URL 设计:别把接口写成谜语

先看个经典反面教材:

GET /getUserInfoById?id=123
POST /user/createNewUser
GET /getAllOrdersForSpecificUser/user/123/page/1/size/20

我第一次看到这种接口,内心是崩溃的。这不是在写接口,这是在出脑筋急转弯。

REST 的精髓是什么?资源 + 动作。URL应该是名词,不是动词。动词是 HTTP 方法的事。

GET /users/123          # 获取用户信息
POST   /users              # 创建用户
PUT    /users/123          # 更新用户
DELETE /users/123          # 删除用户

看到没?清晰得像白开水。别人看一眼就知道这接口是干啥的。

二、HTTP 方法别乱用:你以为 POST 能治百病?

有一种人,写接口永远是 POST,url 全是 /api/xxx,什么场景都是一把梭。

POST /api/getUser
POST /api/deleteUser
POST /api/listOrders

这就跟医生不管什么病都开同一种药一样,迟早要出事。

GET:查,不改资源,幂等的(查一万遍还是那个结果)
POST:增,非幂等(每次创建用户 ID 不同)
PUT:改,全量更新,幂等
PATCH:改,局部更新,非幂等
DELETE:删,幂等(删两次还是没变化)

记住这个原则:能用 GET 就别用 POST,能用 PUT/PATCH 就别把更新写成创建接口。

三、状态码:你的接口在撒谎吗?

很多后端返回的状态码跟接口实际业务完全对不上。查数据找不到,返回200 说"查询成功"——但 data 是空的。这就是睁眼说瞎话。

常用状态码必须分清楚:

2xx 系列(成功):

  • 200 OK — 成功,且有数据返回
  • 201 Created — 创建资源成功(POST成功)
  • 204 No Content — 成功但没内容(DELETE 成功)

4xx 系列(客户端的锅):

  • 400 Bad Request — 参数校验失败、格式错误
  • 401 Unauthorized — 没登录
  • 403 Forbidden — 登录了但没权限
  • 404 Not Found — 资源不存在
  • 409 Conflict — 状态冲突(比如重复提交)
  • 422 Unprocessable Entity — 语义正确但业务逻辑不允许

5xx 系列(服务器炸了):

  • 500 Internal Server Error — 程序出错了,这个状态码要慎用,能处理的业务异常别扔500

四、统一响应格式:没有规矩不成方圆

最怕的是一个项目里接口响应格式五花八门:

# 接口A
{"code": 0, "message": "success", "data": {...}}

# 接口B
{"status": "ok", "result": {...}}
# 接口C
{...}  //裸数据

前端同学看到这种代码,骂人的心都有了。

必须统一。推荐格式:

{
  "code": 0,           # 业务状态码,0=成功,非0=失败
  "message": "success", # 给人类看的提示
  "data": null,         # 数据,失败时为 null
  "requestId": "abc123" # 可选,用于排查问题
}

或者更 RESTful 一点:

{
  "status": "success",
  "data": {...},
  "meta": {
    "requestId": "abc123",
    "timestamp": 1717590400000
  }
}

选一个规范,死磕到底,全项目统一。谁敢乱改格式,直接 pull request 打回。

五、分页、过滤、排序:这三个东西写不好就是灾难

列表接口是最容易翻车的地方。我见过这样的 URL:

GET /orders?userId=123&status=paid&page=1&pageSize=20&sort=createdAt&order=desc&startTime=2024-01-01&endTime=2024-12-31

参数塞了十几个,有的用驼峰有的用下划线,有的日期格式是 ISO 有的是时间戳。

来,定规范:

GET /orders
     ?filter[userId]=123
     &filter[status]=paid
     &sort=-createdAt
     &page[number]=1
     &page[size]=20

解释一下:

  • filter[xxx] — 过滤条件,清晰明了
  • sort=-createdAt — 负号表示降序,正号或不带符号表示升序,这是业界惯例
  • page[number]page[size] — 分页参数,名字统一

还有个问题:返回的分页元数据必须统一。

{
  "code": 0,
  "data": [...],
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 156,
      "totalPages": 8
    }
  }
}

六、版本管理:接口变了老用户怎么办?

接口不可能不变,变了就要考虑兼容性。最蠢的做法是直接在原来的 URL 上改,把老接口直接覆盖掉——等着线上炸吧。

标准做法是加版本号:

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

两种主流策略:

策略一:URL 路径版本(GitHub、Stripe 在用)

GET /v1/users
GET /v2/users

优点:直观,一眼看出调的是哪个版本
缺点:改版需要前端配合迁移

策略二:Header 版本

GET /users
Accept: application/vnd.example.v2+json

优点:URL干净
缺点:不够直观,调试麻烦

我建议用策略一,别跟自己过不去。实际项目中,v1 维护一段时间(比如3-6 个月),给客户端足够时间迁移,再考虑下线。

七、安全:接口裸奔等着被黑?

这条本来不想写,但见得太多了:

  • 接口没有鉴权,所有数据裸奔
  • 敏感信息(密码、token)出现在 URL 参数里,被日志全暴露
  • 没有请求频率限制,被人刷接口直到服务器爆炸

基本要求:

  • 鉴权 token放Authorization Header,不放 URL
  • 敏感操作(登录、支付、修改密码)加二次校验
  • 生产环境必须有限流,服务被打死后悔就晚了
  • CORS 配置好,别让恶意网站能调用你的接口

写在最后

写接口这件事,说难听点,是后端程序员的"门面工程"。接口烂不烂,别人一调用就知道你有没有经验。

好接口的标准是什么?不看文档也知道怎么用。URL 见名知意,状态码准确无误,响应格式统一,分页参数规范——做到这几点,你的接口就已经超过了 90% 的同行。

别做那个写出"神接口"然后跑路让别人维护的人。代码写出来是要被骂的,我劝你善良一点。

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

相关文章

🦞 OpenClaw 生存指南:我是怎么被一只”数字虾”套牢的
自己部署AI工具头秃?小龙虾帮你一键搞定!
我与 OpenClaw 的相爱相杀:一只小龙虾的真实使用报告
别再让你的API文档变成天书了——一个暴躁后端工的忠告
AI调教指南:如何让AI从”人工智障”变成”人工智囊”
你以为监控很牛逼?告警风暴来临时你只会骂娘

发布评论