你写的API是不是一坨屎?——10个让后端开发者崩溃的瞬间

2026-10-03 3 0

你写的API是不是一坨屎?——10个让后端开发者崩溃的瞬间

干了这么多年后端,我见过太多辣眼睛的API了。有的接口返回格式诡异得像外星语言,有的错误处理敷衍得让人想提刀,有的安全漏洞低级得能让人笑出声。

今天我们就来好好吐槽一下,顺便聊聊怎么写出不至于被人骂娘的API。

1. 路径参数和查询参数分不清?你的API已经开始扣分了

先来个基础题:什么情况下用路径参数,什么情况下用查询参数?

资源ID这种明确的东西,放路径里:

GET /users/123
DELETE /orders/456

需要筛选、排序、分页的,放查询参数里:

GET /users?status=active&sort=created_at
GET /products?category=electronics&min_price=100

看起来很简单对吧?但我见过有人把筛选条件塞进路径里:

GET /getUserByStatus/active    ❌ 这什么玩意儿?
GET /getUsers                  ❌ get是什么鬼,RESTful不是这么玩的

名词用复数,路径里不要出现动词。这是最最基础的规范,我都懒得展开讲,但我确实见过太多人连这个都做不好。

2. 你的请求体验证,是不是也在敷衍了事?

我见过最离谱的请求体验证,大概是这样的:

// 前端:{ "name": "", "age": -5 }
后台:OK,存进去了

还有这样的:

POST /users
请求体:{ "email": "这根本不是邮箱" }
响应:200 OK
{
  "message": "success"
}

兄弟,你是认真的吗?这种验证别说保护系统了,连最基本的用户体验都保证不了。正确的做法是:

POST /users
{
  "name": "",
  "email": "not-an-email",
  "age": -5
}

响应:422 Unprocessable Entity
{
  "code": "VALIDATION_ERROR",
  "message": "请求参数验证失败",
  "errors": {
    "name": "用户名不能为空",
    "email": "邮箱格式不正确",
    "age": "年龄必须大于0"
  }
}

每个字段的错误信息清清楚楚,接口调试效率直接翻倍。

3. 状态码选对了没?这不是随便选选的事

200 OK走天下?这是很多后端新手的通病。HTTP状态码是有明确语义的,不是随便返回一个数字。

最常见的几类:

  • 200:成功,但细分下去201是创建成功,204是无返回内容的成功
  • 400:请求参数有问题,不是服务端错误
  • 401:未认证,就是"你谁啊"
  • 403:已认证但没权限,就是"你谁啊但你不许动"
  • 404:资源不存在
  • 422:验证失败,格式对但语义错
  • 429:请求太频繁,稍后再试
  • 500:服务端抽风了,这个真的要尽快修

我见过有人无论什么错误都返回200,然后在body里写个"error": "not found"。这种设计让客户端根本无法区分是业务错误还是网络错误,头疼死调试的人。

4. 分页这个事,说难听点,大部分人做的一塌糊涂

来,看看以下几个场景你有没有中招:

场景A:返回全量数据,让前端自己截

GET /comments?post_id=123

响应:["comment1", "comment2", ..., "comment1000000"]

场景B:用了offset但没有告诉总数

GET /products?offset=100&limit=10

响应:[10条数据]
前端:所以总共多少页?我怎么知道要不要显示"加载更多"?

场景C(最离谱):把分页信息藏在响应结构最深处

响应:{
  "data": [...],
  "meta": {
    "pagination": {
      "total": 1000,
      "per_page": 10,
      "current_page": 11,
      "last_page": 100
    }
  }
}
这嵌套,三级目录,真有你的

一个合理的分页响应应该是这样的:

GET /products?page=2&per_page=20

{
  "data": [...],
  "meta": {
    "total": 158,
    "page": 2,
    "per_page": 20
  },
  "links": {
    "prev": "/products?page=1",
    "next": "/products?page=3"
  }
}

简单直接,一目了然。如果你的分页响应让我要猜,那你已经输了。

5. 错误响应能不能走点心?别就返回一个字符串

这是我在生产环境见过的真实响应(脱敏了,但保证真实):

场景1:
响应:400
"error"

场景2:
响应:400
{}

场景3:
响应:400
{
  "message": "参数错误"
}
好的,参数错误,哪个参数?错在哪里?为什么?

一个认真设计的错误响应应该是这样的:

{
  "code": "INVALID_PARAMETER",
  "message": "请求参数不合法",
  "detail": "字段 email 的格式不正确,应为 user@example.com 的格式",
  "request_id": "req_abc123xyz"
}

code给程序用,方便做错误码枚举和自动化处理。message给开发者看,调试的时候知道发生了什么。detail给终端用户看(需要的话)。request_id让运维定位日志。

每个字段都有它存在的意义,不要偷懒。

6. 认证方式选对了没?Basic Auth在生产环境真的别用了

我知道有人到现在还在用Basic Auth,觉得"能用就行"。能用是能用,但你真的不担心安全问题吗?

认证方式的选择建议:

  • 内部服务间调用:API Key,简单直接
  • 给第三方用:OAuth 2.0,这是标准
  • 用户身份验证:JWT,配合Refresh Token使用

关于JWT,我要特别说几点:

第一,Access Token过期时间要短,15分钟以内比较合理,长了风险大。

第二,Refresh Token要有机制,被盗了要及时发现和撤销。

第三,不要在Token里存敏感信息,Payload是可以被解码的,存个user_id就够了。

// JWT Payload 示例,别存密码之类的东西
{
  "sub": "user_123",
  "role": "admin",
  "exp": 1696300800
}

7. 限流不做,等着被人打挂

先说个真事:我之前公司有个接口没做限流,被某个前端小哥写了个无限循环调用的代码,直接把服务打挂了。从下午三点一直挂到晚上九点,全公司的人在等数据库连接池恢复。

这种低级错误完全可以避免的好吗?

限流的实现方案:

  • 固定窗口:简单,但有临界问题
  • 滑动窗口:更精确,推荐
  • 令牌桶:允许突发流量,推荐用于API
  • Redis:分布式环境下必选

限流了还要告诉客户端:

响应头:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1696300800

触发限流时:
响应:429 Too Many Requests
{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "请求过于频繁,请稍后再试",
  "retry_after": 60
}

8. REST vs GraphQL,先想清楚你真的需要吗

GraphQL刚出来那会儿,一堆人开始疯狂追捧,好像不用GraphQL你的API就落后了一样。

我来泼盆冷水:GraphQL很好,但它解决的是特定场景的问题,不是所有场景都需要它。

适合用GraphQL的场景:

  • 数据关系非常复杂,嵌套层级多
  • 不同客户端需要完全不同的数据结构
  • 客户端需要精确控制返回字段

但对于大多数项目:

  • REST更简单直接,新人上手快
  • HTTP缓存天然支持,CDN直接用
  • 调试方便,Postman/Curl直接调
  • API文档工具成熟,Swagger一键生成

我见过太多公司上了GraphQL,然后发现:团队里没人真正理解它,N+1查询问题一堆,性能反而更差了。技术选型要理性,不要追新追热。

9. API设计最核心的东西,其实和技术无关

说了这么多技术细节,我想聊点更本质的。

一个好的API,它的本质是什么?

我觉得就三句话:

  • 让调用者用起来舒服
  • 出问题了容易排查
  • 边界情况处理得体,不会让人抓狂

做到这三点其实挺难的,需要你对业务有深入理解,需要你愿意花时间打磨细节,需要你始终把"开发者体验"放在心上。

我见过太多后端开发者的心态是"接口能跑就行",从来不考虑调用方的感受。这种态度写出来的API,一定是一坨屎,只是早拉晚拉的区别。

10. 几个我日常坚持的习惯,分享给你

关于缓存:我见过两种极端,一种是完全不用缓存导致数据库压力爆炸,一种是滥用缓存导致数据不一致。我的建议是:读多写少的数据果断缓存,缓存key要有规范,TTL要设置合理。

关于日志:每个请求必须带request_id,这个要贯穿整个调用链路。日志格式用JSON,方便后续检索和解析。敏感信息要脱敏,别把密码日志出来。日志级别要正确,别在生产环境打一堆debug日志。

关于数据库连接池:MySQL默认100连接,PostgreSQL默认10连接,这个数字对于现代微服务来说太少了。我建议用PgBouncer管理连接池,max_connections设置为CPU核心数的2-4倍,监控活跃连接数,设置告警阈值。

最后说几句

后端开发没有捷径,都是踩坑踩过来的。你今天看到的优雅设计,背后可能踩过无数个坑才总结出来的。

重要的是,每次被骂的时候,能不能反思一下,下次能不能做得更好。

API设计这条路,没有终点,只有越来越接近理想中的那个样子。

好了,今天的吐槽就到这里。如果你的API正好中了几条,建议你抽空重构一下,不然哪天被人当面吐槽,那场面真的挺尴尬的。

祝你的接口,稳定长寿命。

相关文章

AI圈最近有点热闹!OpenClaw又整活了,以及那些让我欲罢不能的新玩具
写API这件事,我踩过的坑比吃过的盐还多
重试:本以为是救命稻草,没想到是压死骆驼的最后一根稻草
你的Pod正在被”悄悄枪毙”:K8s资源压力下的驱逐机制全解
还在为部署AI工具秃头?小龙虾帮你一键搞定!
连上了就别断开:一次把HTTP长连接聊透

发布评论