做后端开发这些年,我见过太多API设计得一言难尽。有时候是同事写的,有时候是三年前的自己写的——每次看到都想给当时的自己一巴掌。今天咱们来聊聊RESTful API设计中那些容易翻车的点,顺便看看怎么避坑。
一、HTTP方法乱用:GET干POST的活儿
这个真的太常见了。很多人写接口的时候,GET和POST混着用,纯粹看心情。
// 经典反面教材
GET /api/deleteUser?id=123
GET /api/updateUser?id=123&name=newname
POST /api/getUserInfo
我就想问一句:HTTP协议欠你钱吗?
正确的打开方式是这样的:
DELETE /api/users/123
PATCH /api/users/123 { "name": "newname" }
GET /api/users/123
记住这个原则:GET是读取,POST是创建,PUT是全量更新,PATCH是部分更新,DELETE是删除。如果你发现你的GET请求在干副作用的事儿,那一定是你的设计有问题。
二、状态码随便返回:200表示一切OK?
有些接口,返回200但业务逻辑已经崩了。这不是玄学,这是灾难。
// 错误示范
{
"code": 500,
"message": "服务器爆炸了",
"data": null
}
// 然后HTTP Status Code是200
拜托,500就是500,404就是404,别在body里塞个错误码然后HTTP状态码返回200。这种操作迷惑性极强,前端开发看到body里的code=500会怀疑人生。
正确的做法:
// 业务错误,200 + 业务错误码
HTTP 200
{
"code": 10001,
"message": "余额不足",
"data": null
}
// 真正的服务器错误
HTTP 500
{
"message": "Internal Server Error"
}
简单说:HTTP状态码表示"能不能找到这个接口",业务状态码表示"业务逻辑跑通了没有",各司其职,别串岗。
三、命名随心所欲:拼音+英文混搭风
这个简直是视觉污染。
/api/getUserInfo
/api/getUserList
/api/getUserById
/api/queryUser
/api/fetchUser
/api/retrieveUser
一个项目里五六种获取用户的方式,git blame都不知道该骂谁。RESTful的核心理念是用名词表示资源,用HTTP动词表示操作。所以:
GET /api/users # 获取用户列表
GET /api/users/123 # 获取单个用户
POST /api/users # 创建用户
PUT /api/users/123 # 更新用户
DELETE /api/users/123 # 删除用户
资源是复数形式,操作通过HTTP方法区分,简洁明了。如果你发现你的URL里有get、query、fetch这种动词,那基本可以判定设计有问题。
四、分页参数各玩各的:limit/offset/page/size排列组合
不同接口分页参数完全不一样,这种设计会让前端开发想转行。
// 接口A
/api/users?page=1&pageSize=20
// 接口B
/api/orders?offset=0&limit=20
// 接口C
/api/products?skip=0&take=20
// 接口D
/api/articles?start=0&count=20
统一!统一!统一!重要的事情说三遍。推荐用cursor-based分页或者简单的page+page_size。
// 方案1: 页码式分页(适合数据量稳定的场景)
GET /api/users?page=2&page_size=20
Response:
{
"data": [...],
"pagination": {
"page": 2,
"page_size": 20,
"total": 1000,
"total_pages": 50
}
}
// 方案2: Cursor分页(适合数据频繁变化的场景)
GET /api/users?cursor=abc123&limit=20
Response:
{
"data": [...],
"next_cursor": "def456",
"has_more": true
}
五、版本管理:没有版本的API等于裸奔
很多人觉得"我这次改得不大,不用加版本"。然后改着改着,线上崩了。
API一旦对外暴露,修改就是破坏。正确的版本管理方式:
// URL版本(最直观)
/api/v1/users
/api/v2/users
// Header版本
GET /api/users
API-Version: 2024-01-01
URL版本最直观,调试方便,但很多人觉得丑。Header版本干净,但调试麻烦。看团队喜好选,但一定要有版本管理。
六、错误信息敷衍:"操作失败"三个字打发人
这个错误信息对前端开发来说等于没说。
// 错误示范
{
"message": "操作失败"
}
// 正确示范
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "账户余额不足,当前余额50.00元,需至少100.00元",
"details": {
"current_balance": 50.00,
"required_amount": 100.00
},
"help_link": "https://api.example.com/docs/errors/INSUFFICIENT_BALANCE"
}
}
错误信息要包含:错误码、人类可读的错误描述、可能的解决方案、帮助文档链接。帮别人就是帮自己。
七、安全问题:没有鉴权的API和敞开的门没区别
这个必须单独拎出来讲。
常见的安全问题:
- 敏感数据在URL参数里(GET /api/users?id=123,id直接暴露在日志里)
- 没有做权限校验(任何人都能访问/api/users)
- 没有频率限制(接口分分钟被刷爆)
- CORS配置成*(生产环境千万别这么干)
基础安全检查清单:
// 1. 敏感操作必须鉴权
POST /api/orders → 需要 Bearer Token
// 2. 参数校验要严格
{
"email": "not-an-email",
"age": -5
}
→ 400 Bad Request + 详细校验错误信息
// 3. 敏感数据要脱敏返回
{
"phone": "138****5678",
"id_card": "110101**** **** 1234"
}
// 4. 频率限制要合理
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1640000000
总结
好的API设计就像是好的产品设计:让人用起来舒服,出问题的时候能快速定位,扩展的时候不需要伤筋动骨。
几个核心原则:
- HTTP方法要对,别把GET当POST用
- 状态码要准确,别200表示一切崩溃
- 命名要统一,别一个项目五六种风格
- 分页要一致,别让前端猜
- 版本要管理,别让改版成灾难
- 错误信息要详细,别就俩字打发人
- 安全要上心,别让接口裸奔
API设计这事吧,说是技术活儿,但更像是细心活儿。多考虑使用者的感受,少一点"能用就行"的心态,你的API质量能上升一个档次。
毕竟,我们都希望别人调用我们的接口时,心里想的是"这API写得真舒服",而不是"写这个接口的人是不是脑子有坑"。
共勉。