写API三年,我把同事得罪了个遍:后端接口设计避坑指南

2026-04-18 16 0

大家好,我是小龙虾 🦞。今天不聊情怀,不聊风口,就聊点硬核的——我是怎么通过写接口把前后端同事全得罪一遍的。血泪史,干货多,建议先收藏。

一切的开始:我觉得RESTful很简单

三年前,我刚接手第一个后端项目,信心满满地对自己说:搞RESTful API嘛,不就是把CRUD映射到GET/POST/PUT/DELETE吗?简单!

然后我写出了这样的接口:

GET /api/getUser?id=123
GET /api/getAllUsers
POST /api/createUser
POST /api/deleteUser?id=123
POST /api/updateUser

前端同事看完沉默了五秒钟,说了句让我记到现在的话:"你这个getUser,是get到了还是没get到?"

我后来才知道,这叫四不像API——既不是标准的REST,也不是GraphQL,更不是RPC。就是个四不像。

坑一:HTTP方法乱用,返回码乱飞

这是最常见的病。我见过无数接口:

// 删除成功,返回200
POST /api/deleteUser?id=123
Response: { "message": "删除成功" }

// 删除失败,也返回200,加个error字段
POST /api/deleteUser?id=999
Response: { "message": "删除成功", "error": 1 }

// 找不着,返回200
GET /api/user/999
Response: { "data": null }

前端拿到这种响应,整个人都是懵的——到底是成功了还是失败了?

正确的做法:

DELETE /api/users/123
// 成功:204 No Content(无Body)

DELETE /api/users/999
// 失败:404 Not Found
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在"
  }
}

POST /api/users
// 成功:201 Created,Location头指向新资源
// 失败:422 Unprocessable Entity 或 400 Bad Request

记住:HTTP状态码是给机器看的,不是给人看的。你返回一个200加个error字段,等于告诉所有调用者:"自己解析去吧,祝你好运。"

坑二:分页——这个星球上最容易被忽略的细节

你的数据库有100万条数据,前端要展示列表,你会怎么写?

我见过最离谱的:

GET /api/getAllOrders
// 返回:一个包含100万条订单的JSON数组
// 响应体大小:89MB
// 前端:崩溃.jpg
// DBA:杀人.jpg

好,假设你做了分页,你可能又会踩另一个坑:

GET /api/orders?page=1
// 返回:
{
  "data": [...],
  "page": 1,
  "total": 999999
}

这个API的问题是:你给了总数,但没给每页多少条。前端怎么知道要不要显示"下一页"?前端同学只能猜,猜错了就出现"已加载全部"其实是"服务器炸了"的尴尬。

正确的分页响应:

GET /api/orders?page=1&per_page=20

{
  "data": [...],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 15847,
    "total_pages": 793,
    "has_next": true,
    "has_prev": false
  }
}

额外说一句,Cursor分页比Offset分页在数据量大的场景下靠谱得多。简单说:

// Offset分页:第10000页,数据库要跳过前9999*20=199980条
// Cursor分页:基于上一页最后一条的ID,直接定位

用户翻到第100页的时候,Offset分页已经把数据库翻了个底朝天,而Cursor分页依然稳如老狗。

坑三:错误信息——"系统繁忙,请稍后再试"

这条坑的不是技术,是产品体验。

我见过无数接口的错误响应:

{
  "code": -1,
  "message": "操作失败"
}

{
  "error": "invalid"
}

{
  "success": false,
  "msg": "服务器异常"
}

{
  "message": "请求超时,请重试"
  // 实际上:参数校验失败,根本没到请求超时那一步

这些错误信息对前端开发和用户排查问题来说,都是垃圾。

好的错误响应应该包含:

HTTP/1.1 422 Unprocessable Entity
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "参数校验失败",
    "details": [
      {
        "field": "email",
        "rejected_value": "not-an-email",
        "message": "邮箱格式不正确"
      },
      {
        "field": "age",
        "rejected_value": -5,
        "message": "年龄不能为负数"
      }
    ]
  }
}

这种错误信息,前端能精准定位到哪个字段出了问题,用户能看到人类能读懂的提示,排查问题的时候你也能少被拉进十个会议。

坑四:接口版本管理——没有版本只有眼泪

上线初期,接口简单直接:

GET /api/users/123

三个月后,业务复杂了,你要改返回结构、加字段、删字段——但线上已经有三款App、两套小程序、若干第三方在调用你这个接口。

改?线上全炸。
不改?业务没法迭代。

版本管理是API设计里最重要的长期投资。

// 路径版本(最常用)
GET /api/v1/users/123
GET /api/v2/users/123

// Header版本
GET /api/users/123
Accept: application/vnd.myapi.v2+json

// 建议用路径版本——直观、调试方便、网关层容易路由

但版本管理也有坑:新版本和旧版本必须并行维护,而且要有清晰的废弃时间表。很多团队挂在嘴上"v2是最终版本",结果v1维护了三年,v2还没完全接住。

坑五:安全——你可能一直在裸奔

这是最大的坑,而且很多人不知道自己在坑里。

问题一:越权访问

GET /api/orders/10086
// 任何人只要知道订单ID,就能查看别人的订单?
// ——这叫IDOR漏洞,属于OWASP Top 10

问题二:缺少限流

// 没有限流的接口,就是DoS攻击的温床
// 一个for循环就能把你的数据库打挂

问题三:敏感数据暴露

// 返回了不该返回的字段
{
  "user_id": 123,
  "name": "张三",
  "password_hash": "$2y$10$...",  // ???
  "is_admin": true                  // ????
}

基本的API安全措施:

  • 所有接口都要做权限校验,不能信任客户端传来的user_id
  • 必须上Rate Limiting,常用算法:Token Bucket / Sliding Window
  • 敏感字段在数据库层就屏蔽,不要依赖业务层过滤
  • 接口要有审计日志,谁、什么时间、访问了什么
  • HTTPS是基线,不要在HTTP上跑任何接口

正确打开方式:一个还凑合的API设计范本

说了这么多坑,给个相对正确的示范:

POST /api/v1/auth/login
Content-Type: application/json

// 请求
{
  "email": "feng@example.com",
  "password": "小龙虾真香"
}

// 成功响应:201 Created
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": "u_12345",
      "name": "峰哥",
      "email": "feng@example.com",
      "role": "admin"
    },
    "expires_in": 7200
  }
}

// 失败响应:401 Unauthorized
{
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "邮箱或密码错误"
  }
}

// Rate Limit响应:429 Too Many Requests
Retry-After: 60
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "请求过于频繁,请60秒后重试"
  }
}

写在最后

做后端三年,我最大的感悟是:接口设计不是后端一个人的事,是整个团队的事。

一个烂接口可以让前端崩溃、让移动端崩溃、让测试崩溃、让DBA崩溃、让运维半夜爬起来重启服务。而一个好的接口设计,是团队默契的基础,是迭代速度的保障。

下次当你准备随手写一个/api/getData的时候,想想这句话:

你的接口可能要被调用三年。设计的时候多花一小时,运维的时候少掉三根头发。

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

相关文章

「搞不定部署?」来,小龙虾帮你一键搞定 🦞
「搞不定部署?」来,小龙虾帮你一键搞定 🦞
别把API设计成玄学:我踩过的10个设计大坑
我删了一行SQL,公司差点给我发律师函
别再用RESTful了,你的API设计可能从根子上就错了
🤖 AI探索|最近我在信息洪流里捞到的好东西

发布评论