RESTful API设计:那些年我们一起踩过的坑,今天一次说清楚
干这行这么多年,我见过太多团队在API设计上翻车。有的返回格式乱成一锅粥,有的一致性差到让人怀疑人生,还有的接口文档写得跟悬疑小说似的——看到最后也不知道这破接口到底咋用。
今天不整虚的,就把RESTful API设计中最常见的坑拉出来遛遛,顺便给出我的避坑指南。纯属实战经验,拿去即用。
一、URL设计:你的路径是给人看还是给机器看?
先来看一个反面教材:
GET /getUserInfo?userId=123
POST /modifyData
GET /queryAllProductInfo
看到这种接口,我内心是崩溃的。这哪里是RESTful,这分明是在写口语化描述。
RESTful的核心是资源导向。URL应该是名词,不是动词。动词应该体现在HTTP方法里。
正确姿势:
GET /users/123 # 获取用户
PUT /users/123 # 更新用户
DELETE /users/123 # 删除用户
GET /users/123/orders # 获取用户的订单列表
记住一个原则:URL是地址,不是命令。你说"去超市买牛奶"不能说成"去买东西用钱换牛奶的超市在东边那个路口",对吧?一样的道理。
二、状态码:别总返回200然后在body里说"失败了"
这个坑我见过无数次了:
HTTP/1.1 200 OK
{
"code": 500,
"message": "服务器内部错误"
}
兄弟,你这是跟我玩狼人杀呢?HTTP状态码是给调用方判断用的,你返回200但body里说失败,调用方得先解析body才能知道出事了——这完全违背了HTTP状态码的设计初衷。
标准做法:
200 OK # 成功
201 Created # 创建成功(POST/PUT返回)
204 No Content # 删除成功,无返回body
400 Bad Request # 参数错误
401 Unauthorized # 未认证
403 Forbidden # 无权限
404 Not Found # 资源不存在
500 Internal Server Error # 服务器错误
有人会说:"有些业务错误码需要细粒度控制,比如'余额不足'和'转账限额超限'都是400,调用方没法区分。"
好问题。解决方案是在body里放业务错误码,但HTTP状态码要先对:
HTTP/1.1 400 Bad Request
{
"code": "INSUFFICIENT_BALANCE",
"message": "余额不足",
"detail": {
"currentBalance": 50.00,
"requiredAmount": 150.00
}
}
状态码负责大类,body里的code负责细分。各司其职,清晰明了。
三、分页:要么不做,要么做完整
很多API的分页实现堪称行为艺术:
// 方案A:只有数据,没有分页信息
{
"users": [...]
}
// 方案B:返回总数,但没告诉用户怎么用
{
"users": [...],
"total": 12345
}
// 方案C:假装分页
{
"users": [...], // 其实一次返回了全部
"page": 1
}
正确的分页应该包含:当前页、每页条数、总数、是否有下一页。
{
"data": [...],
"pagination": {
"page": 2,
"pageSize": 20,
"total": 158,
"totalPages": 8,
"hasNext": true,
"hasPrev": true
}
}
还有个关键点:游标分页 vs 偏移分页。数据量小用偏移分页,简单直观;数据量大或者需要实时性,用游标分页(基于ID或时间戳),避免深分页性能问题。
四、版本管理:没有版本控制的API是在耍流氓
我见过最离谱的版本管理是——没版本。接口直接改,客户端直接崩。
版本控制的三种主流方式:
// 方式1:URL路径(最常见)
GET /v1/users/123
GET /v2/users/123
// 方式2:Header(更"优雅"但不便调试)
GET /users/123
API-Version: 2023-01-01
// 方式3:Query参数(不推荐)
GET /users/123?version=2
我的建议是用URL路径。理由:直观、可测试、可追踪、出问题排查快。
版本升级的原则:老版本尽量保持时间长一点,给调用方足够的迁移时间。别像某些大厂一样,接口说下线就下线,一点情面不讲。
五、接口文档:写得烂的文档比没文档更可怕
很多团队觉得有Swagger/OpenAPI就算有文档了。太天真了。
一份合格的API文档必须包含:
- 请求示例和响应示例(真实的,不是placeholder)
- 每个字段的类型、含义、是否必填
- 错误码说明(不只是code,还要有解决方案)
- 业务场景描述(这个接口是干啥的,什么时候用)
- 认证方式(OAuth2? API Key? JWT?)
我推荐Knife4j(Java系)或者Swagger UI,配合Apifox做团队协作。工具是其次,关键是内容。
六、安全:基础不做好,后面全是泪
这块很多人觉得是老生常谈,但我见过太多生产环境的翻车:
- 接口没鉴权,数据裸奔
- 敏感信息(密码、token)直接放URL里,被日志全暴露
- 没做请求限流,被人一顿狂刷服务器就炸了
基础安全三件套:
1. HTTPS必须(别省那个证书钱)
2. 认证鉴权必须(JWT/OAuth2/签名)
3. 参数校验必须(前后端都要做,后端不能信前端)
4. 限流熔断必须有(防止DDoS和系统过载)
还有个小细节:错误信息不要暴露内部细节。比如"密码错误"和"用户不存在"要统一返回"用户名或密码错误",不然你就在帮黑客缩小攻击范围。
写在最后
API设计看起来是技术活,其实本质是产品思维——你的接口是给别人用的,要站在调用方的角度思考。
好的API设计有这么几个特点:
- 自解释:看URL就知道干啥
- 一致性:风格统一,减少学习成本
- 容错性:输入不规范也能优雅处理
- 可追踪:日志、监控、告警齐全
如果你接手一个烂摊子API,别急着重构。先画个架构图,理清依赖关系,然后小步快跑,逐步演进。一口气推倒重来的结果通常是一起重来。
有问题评论区见,我尽量回。觉得有用的,点个在看,我们下篇见。