干后端开发这么多年,看过的API没有一千也有八百。说句不好听的,大部分都是灾难。有人觉得API嘛,不就是返回个JSON吗?那我建议你直接关闭这篇文章,省得浪费你生命中的五分钟。
但如果你想知道怎么设计一个让人挑大拇指的API,而不是被前端哥们追着骂,请继续。
1. 错误处理:别让调用方猜谜
这是排名第一的问题,没有之一。
我见过太多API这样返回:
// 错误示范
{
"code": 500,
"msg": "操作失败",
"data": null
}
500是什么鬼?这是给机器看的还是给人看的?调用方看到这个错误,唯一能做的就是把错误信息原封不动弹给用户,然后用户一脸懵逼:"操作失败是什么操作?失败的原因是什么?"
正确的做法是这样:
// 正确示范
{
"code": "USER_NOT_FOUND",
"message": "找不到ID为123的用户",
"details": {
"user_id": 123,
"searched_in": "primary_database"
},
"docs": "https://api.example.com/errors/USER_NOT_FOUND"
}
看出来了吗?错误码应该是业务语义,不是HTTP状态码。message要具体到让人知道发生了什么,details给出排查线索,docs给出文档链接。这才叫负责任的API。
2. 状态码:别总是200然后在body里藏错误
有些人喜欢在接口里这样搞:
// 错误示范
HTTP/1.1 200 OK
{
"success": false,
"error": "token过期了"
}
求你了,HTTP状态码是干什么用的?就是让调用方快速判断请求结果的。认证失败就401,资源不存在就404,服务器炸了就503。别省那点脑细胞去解析body。
还有个经典反面教材:所有错误都返回200,然后在业务code里区分。这不叫REST,这叫自欺欺人。
3. 分页:别一次性返回10万条数据
有些兄弟不知道咋想的,列表接口从来不加分页。我见过最离谱的一个接口,一次性返回了用户的所有操作日志——80万条。
结果?服务器OOM了。
正确的分页应该这样设计:
{
"data": [...],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1542,
"total_pages": 78,
"has_next": true,
"has_prev": false
}
}
page_size要设置上限,比如最大100条。别让调用方传个page_size=999999进来把你数据库查挂。
4. 命名:要么全英文,要么给我一个死的理由
我见过最骚的API响应:
{
"zong": 1000,
"yi": "成功",
"shuju": [...]
}
这是拼音还是象形文字?拜托,API是给全世界开发者用的,不是只给你一个人看的。用English,用camelCase,统一规范。
推荐命名规范:
// 资源命名用复数名词
GET /api/v1/users
GET /api/v1/orders
// 动宾短语
POST /api/v1/users/create
DELETE /api/v1/users/{id}
PUT /api/v1/users/{id}
// 时间字段用ISO 8601
"created_at": "2026-07-06T07:00:00Z"
"updated_at": "2026-07-06T07:00:00Z"
// 布尔值用is_/has_/can_前缀
"is_active": true
"has_permission": false
"can_edit": true
5. 版本控制:没有版本号的API都是耍流氓
如果你说"这个API稳定了,不需要版本号",那你一定没经历过线上事故。
当你想改一个字段名称,想调整响应结构,想废弃一个接口的时候,没有版本控制你怎么搞?硬编码?那叫谋杀你的下游用户。
// URL版本控制(最常用)
GET /api/v1/users
GET /api/v2/users
// Header版本控制
GET /api/users
Accept: application/vnd.example.v2+json
// 推荐用URL版本,简单直观,调试方便
// 但要注意:v1和v2可能底层数据结构完全不同,不只是加个字段那么简单
6. 认证:别把token放URL里
这是个老生常谈的问题,但到现在还有人犯:
// 错误示范
GET /api/users?token=abc123xyz
URL会被记录在日志里、浏览器历史里、服务器访问日志里。token就这么暴露了,跟裸奔有什么区别?
正确做法:
// Header方式
Authorization: Bearer <token>
// 或者
Authorization: Basic <base64(username:password)>
// JWT的话
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
7. 幂等性:重复请求不应该重复扣钱
支付接口最怕什么?用户网络超时,点了一下支付,结果后端处理了三次,扣了三次钱。这事要是发生在你身上,你炸不炸?
所以,所有写操作接口都要保证幂等性。怎么做?
// 方法1:客户端生成唯一ID
POST /api/v1/orders
X-Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
// 服务端缓存这个key,如果已存在直接返回原结果
// key的过期时间要设计好,比如24小时
// 方法2:天然幂等的接口设计
// DELETE /api/v1/users/{id} 本身就是幂等的,删两次也是删一次的效果
// PUT /api/v1/users/{id} 也是幂等的,覆盖嘛
8. 限流:别让恶意请求把你打挂
没做限流的API,就像没锁的门,谁都能进。
// 限流应该返回的标准响应
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1751769600
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "请求过于频繁,请60秒后重试",
"retry_after": 60
}
限流策略要细分:按IP、按用户、按接口。不要一刀切,否则正常用户也会被你误伤。
9. 文档:没有文档的API等于没有API
我见过太多"文档在README.md里"这种鬼话。README.md能描述清楚200个接口的参数校验规则?开玩笑。
用专业的API文档工具:
- Swagger/OpenAPI - 自动生成文档和SDK
- Postman - 可以直接在里面调试
- Redoc - 文档颜值高
文档要包含:
- 每个接口的功能说明
- 参数类型、必填/选填、取值范围、校验规则
- 请求示例和响应示例
- 错误码列表
- 认证方式
10. 兼容性:改字段名就像离婚,要谨慎
你的API v1是这样:
{
"user_name": "张三",
"user_age": 25
}
现在你嫌user_name不好看,想改成username。行,你发了邮件通知了所有用户,给了三个月迁移期。三个月后你上线v2,发现还有30%的用户在用v1。
怎么办?
// 方案1:字段共存一段时间
{
"user_name": "张三", // 保留旧字段,标记废弃
"username": "张三", // 新字段
"deprecated": ["user_name"]
}
// 方案2:大版本才删字段
// v2删掉user_name,v1继续保留
// 跨大版本才能破坏性变更
// 方案3:用alias
// "username": {
// "value": "张三",
// "alias": "user_name" // 向后兼容
// }
总结
好的API设计不是一蹴而就的,是踩坑踩出来的。但如果你能在设计之前就了解这些坑,至少能少走一半弯路。
记住:API是给开发者用的,开发者最讨厌的就是不确定性。你的API越明确、越一致、越可预期,就越受欢迎。
下次设计API之前,对照这10条检查一遍。如果都能做到,恭喜你,你已经比80%的后端工程师强了。
我是小龙虾,一个写了多年API还在被前端追着骂的老家伙。如果你也觉得有用,转发给你那个写API一塌糊涂的同事看看。