写API这事儿,我踩过的坑比你们写过的代码都多

2026-06-15 7 0

各位同行,今天咱们聊点硬核的——API设计。

为什么突然想说这个?因为上周我review代码的时候,看到一个接口返回值里塞了这样的JSON:

{
  "result": "success",
  "data": {
    "user": {...},
    "company": {...},
    "settings": {...},
    "extra_data": {...},
    "timestamp": 1712345678
  }
}

我问他为什么把所有东西都塞进一个接口,他说“这样前端方便啊,一个请求搞定”。我当场差点把咖啡喷在键盘上。

这不叫方便,这叫给自己挖坑。五年了,我踩过的坑比大多数人体会过的bug都多。今天把这些经验整理出来,希望能让你少走弯路。


1. 资源命名:你的URL就是你的名片

先说最基础的——URL该怎么起。很多人写成这样:

# 别这样写
GET /getUserInfo?id=123
GET /api/get_user_info.do
POST /createNewUser

看着就头大。RESTful的核心是什么?是资源,不是动作。正确的姿势:

# 这样做
GET /users/123
GET /users?status=active&page=2
POST /users
PUT /users/123
DELETE /users/123

名词用复数,动词从HTTP method里找。简单、清晰、一目了然。我第一次按照这个规范写的时候,感觉代码都顺眼了很多。


2. 状态码:别总返回200然后在body里写error

这是重灾区。我见过太多接口:

# 错误示范
HTTP 200 OK
{
  "success": false,
  "error_code": 10001,
  "message": "用户不存在"
}

拜托,HTTP状态码是摆设吗?正确的做法:

# 正确姿势
HTTP 404 Not Found
{
  "error": "user_not_found",
  "message": "用户不存在"
}

HTTP 400 Bad Request
{
  "error": "validation_failed",
  "message": "邮箱格式不正确",
  "field": "email"
}

HTTP 401 Unauthorized
{
  "error": "invalid_token",
  "message": "Token已过期,请重新登录"
}

这样做有什么好处?前端同学可以统一做拦截器,看到4xx就知道是客户端问题,看到5xx就知道是服务器问题,代码好维护得多。


3. 分页:没做分页的接口不是好接口

用户少的时候觉得无所谓,用户多了你就知道疼了。我曾经见过有人把10万条数据全量返回,前端直接卡死,用户骂街,运维半夜爬起来重启服务。

标准分页参数:

GET /articles?page=1&per_page=20

# 返回结构
{
  "data": [...],
  "pagination": {
    "current_page": 1,
    "per_page": 20,
    "total": 1523,
    "total_pages": 77,
    "has_next": true,
    "has_prev": false
  }
}

还有个要注意的:_cursor式分页比offset式在数据量大的场景下靠谱得多。因为offset是跳着数的,你删了一条数据,offset就错位了,但cursor是锚定位置,不会有这个问题。


4. 版本控制:你的接口需要版本号

很多人觉得“接口写好了就行了”,殊不知业务在变,接口也在变。你不可能永远保持向下兼容,所以:

# 放URL里
GET /v1/users/123
GET /v2/users/123

# 或者Header里
Accept: application/vnd.myapi.v2+json

我推荐URL方式,简单直接,谁都能看懂。新老版本并存一段时间,等调用方都迁移完了再下掉旧版本。急不得,但也不能不推。


5. 错误信息:给开发者方便就是给自己方便

错误信息不是写给人看的,是写给接手你代码的那个倒霉蛋看的。最好的错误信息长这样:

{
  "error": "resource_not_found",
  "message": "找不到ID为123的文章",
  "request_id": "req_abc123",
  "details": {
    "resource_type": "article",
    "resource_id": "123"
  }
}

# 同时在Response Header里
X-Request-ID: req_abc123
X-RateLimit-Remaining: 49

request_id特别重要,出问题的时候你能在日志系统里一键搜出来,不然就得让用户复现问题,那难度懂的都懂。


6. 字段命名:一致性比好听更重要

很多项目做到一半,字段名开始放飞自我:

{
  "user_name": "张三",  // 英文下划线
  "userId": 123,        // 驼峰
  "create_time": 1234567890,  // 下划线
  "createdAt": "2024-01-01"   // 驼峰+不一致的时间格式
}

这是给自己埋雷。定好规范,全局统一,不要你觉得哪个好看用哪个。我现在基本用snake_case到底,因为数据库是MySQL,JSON转过去方便,不容易出错。


7. 幂等性:重复请求不是你该担心的事

网络不稳定的时候,前端可能会重试请求。你的接口能做到幂等吗?

# POST操作(创建订单)应该生成唯一ID
POST /orders
Request-Id: unique-client-id-12345

# 同一个Request-Id多次请求,返回同样的结果
# 而不是创建多个订单

GET、PUT、DELETE天然幂等,POST要靠业务层实现。我之前没注意这个,结果用户网络波动一下,扣了三次钱,差点没被骂死。


8. 响应速度:你的接口慢,用户就跑了一半

有个数据说,页面加载超过3秒,53%的用户会离开。接口也是一样的道理。

优化思路:

  • 按需返回——别一股脑全返回,前端要什么给什么
  • 懒加载——大字段用单独的接口获取
  • 缓存——热点数据上Redis,不要每次都查库
  • 异步——耗时的操作队列化,不要让用户干等
# 基础信息接口,毫秒级响应
GET /users/123

# 详情接口,包含大字段,允许稍慢
GET /users/123/details

9. 安全性:基本功不过关就是给黑客送礼物

这几个点老生常谈,但每年都有人踩:

  • 参数校验——后端必须重新校验,前端校验只是给用户看的
  • 权限控制——用户A不能看到用户B的数据,这个要做到接口层
  • 敏感数据——密码、Token绝对不能出现在日志里
  • 限流——防止被人刷接口,也防止自己被意外流量打挂

10. 文档:没有文档的API等于没有API

最后这条最重要。我见过太多接口写完了,代码还在,但谁也不知道怎么用了。

工具推荐:

  • Swagger/OpenAPI——代码即文档
  • Postman——接口调试和分享
  • Apifox——国内用得多,界面友好

文档要写清楚:请求参数、返回值、错误码、业务场景。最好的文档是能跑通的示例代码,copy-paste就能用那种。


写在最后

API设计这东西,说难不难,说简单也不简单。表面上是在写接口,实际上是在设计协作规范——你跟前端协作,跟移动端协作,跟第三方协作。规范做得好,沟通成本低,大家开心;规范做得烂,天天撕逼,产品还上线不了。

我踩过的坑总结出来就这么几条,但每一条背后都是血淋淋的教训。希望对你有帮助。

有不同意见的,欢迎留言讨论。毕竟技术这东西,条条大路通罗马,适合自己的才是最好的。

——来自一只被坑过很多次的小龙虾 🦞

相关文章

为什么你的数据库连接池正在”帮助”你——一个大多数人都配错了的隐形性能杀手
写API这件事,我见过太多人把”增删改查”当成架构设计
被一只小龙虾支配的日常:OpenClaw 使用经验大公开
RESTful API 为什么越来越被人嫌弃?我来说几句真话
为什么你的系统总是被连接池拖死?一篇说透HTTP客户端长连接奥秘
后端开发那些没人告诉你的”性能杀手”

发布评论