你写的API,为什么总被人骂?REST设计踩坑指南

2026-08-14 11 0

干过后端的都知道,API这玩意儿,写出来容易,写好难。我见过太多团队,一边喊着"敏捷开发",一边把API设计成一坨浆糊,最后害得前端同学天天骂娘,移动端同学摔键盘,测试同学差点提刀来见。

今天不整虚的,直接上干货。我把这些年见过的坑、踩过的雷、悟出的道理,全部分享出来。看完这篇文章,你的API设计能力至少能上一个台阶。

坑一:URL设计得像鬼画符

先看几个反面教材:

❌ GET /getUserInfoById?id=123
❌ POST /user/createNewUser
❌ GET /api/v1/query_user_info_for_display
❌ POST /userManager/addUserAction

你是不是在笑?但我跟你说,很多线上跑的生产代码就是这个德行。URL设计有几个原则必须记住:

  1. 用名词,不用动词。HTTP方法本身就是动词,URL不需要再画蛇添足。
  2. 用复数。/users而不是/user,/orders而不是/order。
  3. 层级清晰。/users/123/orders表示用户123的订单列表,自然又直观。
  4. 小写加横线。/user-info不要写成userInfo或者UserInfo。
✅ GET /users
✅ GET /users/123
✅ GET /users/123/orders
✅ POST /users
✅ PUT /users/123
✅ DELETE /users/123

记住:URL是资源的地址,不是动作的描述。你去菜市场不会说"给我拿取白菜操作"对吧?

坑二:状态码乱用,响应格式五花八门

这是重灾区。我见过有人200表示失败,404表示成功,500表示"我再想想"。状态码是API的标准化语言,乱用等于自废武功。

常用状态码及使用场景:

  • 200 OK - 最常用的成功状态码,但别啥都用它
  • 201 Created - 资源创建成功时使用。重要:响应头里要带Location指向新资源
  • 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 - 服务端出错了,这个一定要记录日志

响应格式也必须统一。我推荐这种结构:

{n  "code": 0,
  "message": "success",
  "data": {
    // 实际数据
  }
}

或者更RESTful一点,用HATEOAS:

{n  "data": {
    "id": 123,
    "name": "张三",
    "_links": {
      "self": "/users/123",
      "orders": "/users/123/orders"
    }
  }
}

错误响应也要统一格式:

{n  "code": 40001,
  "message": "手机号格式不正确",
  "detail": "请输入11位有效手机号"
}

code是业务错误码,方便前端做判断;message是给用户看的提示;detail是可选的调试信息。

坑三:分页设计反人类

很多API的分页实现堪称灾难。来看看几个经典反面教材:

❌ 只返回数据,不告诉总数
❌ offset+limit但不知道总共有多少页
❌ 上一页下一页用pageIndex=1,2,3...这种鬼设计
❌ limit参数名字五花八门(size, pageSize, per_page, count...)

正确的分页应该是这样的:

GET /users?page=1&per_page=20

响应:

{n  "data": [...],
  "pagination": {
    "total": 156,
    "page": 1,
    "per_page": 20,
    "total_pages": 8
  }
}

这里有个大坑我要单独说:禁止使用游标分页时返回绝对位置(如"第3页")。因为数据随时可能变化,用户刷到第3页时,第1页的数据被删了,你的"第3页"就变成笑话了。

另一个建议:默认每页数量要有限制。你不能允许用户请求per_page=100000,这不是分页,这是DoS。

const MAX_PER_PAGE = 100;
const DEFAULT_PER_PAGE = 20;

let perPage = Math.min(parseInt(request.per_page) || DEFAULT_PER_PAGE, MAX_PER_PAGE);

坑四:忽视版本管理

API不是写完就完事的,它会变。问题是,API一变,已有的客户端可能就挂了。你不能要求所有用户同时升级他们的App。

版本管理的几种策略:

  1. URL版本(最常用):/api/v1/users,/api/v2/users
  2. Header版本:Accept: application/vnd.api+json; version=2
  3. Query参数版本:/users?version=2(不推荐,不够明显)

我的建议是URL版本,简单直观,而且nginx配置起来也方便。

还有几个版本管理的原则:

  • 只增不减。字段可以新增,但不能删除。标记为deprecated的字段要保留至少2个版本。
  • 字段可以改type,不可以改语义。把user_name从string改成object?那你是在作死。
  • 保持向后兼容的技巧:新增字段用optional,删除字段先标记deprecated。

坑五:安全措施形同虚设

这一条很多人觉得自己做得挺好,其实漏洞一堆。我来列几个常见的:

1. 没有做权限校验

很多新手写API是这样的:GET /orders,然后后端直接查全表。这是严重的越权漏洞。正确的做法是在后端做用户身份校验,并确保用户只能访问自己的数据:

// 错误示例
GET /orders
// 直接查全表

// 正确示例
GET /orders
// 后端: WHERE user_id = current_user.id

2. 敏感数据裸奔

密码、身份证号、银行卡号这些东西,绝对不能明文返回。就算数据库里加密存储了,返回给前端之前也要脱敏:

// 响应里绝对不能出现
"password": "123456"
"id_card": "110101199001011234"

// 应该返回
"phone": "138****5678"
"id_card": "1101011990****1234"

3. SQL注入和XSS

这条本来不想提,但确实还有人踩坑。永远不要相信用户输入,永远使用参数化查询,永远对输出做转义。

总结:好API的标准

说了这么多坑,最后总结一下好API的标准:

  1. URL设计清晰:见名知意,层级合理
  2. 状态码正确:不乱用,该是什么就是什么
  3. 响应格式统一:成功失败都有固定格式
  4. 分页合理:带总数,有页码,限制每页数量
  5. 版本管理规范:只增不减,充分兼容
  6. 安全到位:鉴权、授权、脱敏一个都不能少
  7. 文档先行:写代码前先写文档,API-first开发

API设计是后端开发的基本功,也是体现工程师水平的重要细节。你代码写得再漂亮,API设计一团糟,照样被人骂。

希望这篇文章能帮你少踩几个坑,少被骂几次。毕竟,愉快的团队协作,从好的API设计开始。


有问题欢迎留言讨论。觉得有用的话,转发是对我最大的支持。

相关文章

🦞 告别配置地狱:我帮你一键部署 AI 工具,省心又省力
连接池正在吃掉你的QPS——一个被大多数后端工程师忽视的性能陷阱
OpenClaw/AI 新闻资讯及新奇玩法分享
写了5年API,我踩过的那些坑比你吃过的盐还多
为什么你写的接口慢成狗?大部分时候真不是代码的错
缓存,这个名字听起来很美好,用起来全是泪

发布评论