我见过太多项目,技术选型阶段雄心勃勃,上线之后鸡飞狗跳。不是代码写得烂,是从第一天起就埋了雷。今天不聊虚的,就说说那些让后端开发者夜不能寐的RESTful API设计坑。
坑一:把所有东西都塞进GET和POST
很多程序员(尤其是从PHP转过来的)有个根深蒂固的观念:HTTP只有两种方法,GET和POST。其他的?不存在。
结果呢?删除资源用POST /deleteUser?id=123,更新用POST /updateUser,查询还是用POST,美其名曰"安全"。
你说这玩意儿叫RESTful?我叫它"把HTTP协议当摆设"。
HTTP语义是干嘛用的?是让整个互联网都遵循同一套规则。你写DELETE /users/123,全世界的中间件、缓存、CDN、防火墙都知道这是在删东西。你写POST /deleteUser,它们只会一脸问号。
坑二:HTTP状态码?那是什么,能吃吗
见过最离谱的接口是这样的:
{
"code": 200,
"message": "success",
"data": null
}
然后:
{
"code": 500,
"message": "服务器炸了,快跑",
"data": null
}
不是,哥们儿,HTTP有自己的状态码体系啊!200是OK,201是Created,400是Bad Request,401是Unauthorized,403是Forbidden,404是Not Found,500是Internal Server Error……
你自己又包一层code,这不就是套娃行为吗?客户端得先判断HTTP状态码,再判断你的code,双重浪费。
正确的做法:直接用HTTP状态码,body里只放业务数据。除非你的业务错误码和HTTP语义强耦合,否则别画蛇添足。
坑三:URL命名放飞自我
来看一个真实的接口列表(我瞎编的但你肯定见过):
/api/get_user_info
/api/queryUserById
/api/user/query
/api/userInfo
/api/getUser
同一套业务,五种命名风格。前端开发者每次接接口都要问:"这个是用哪个?"后端呢,每次新写接口都要纠结:"我该用哪个风格?"
RESTful的URL应该名词复数,层级清晰:
GET /users # 获取用户列表
GET /users/{id} # 获取单个用户
POST /users # 创建用户
PUT /users/{id} # 更新用户
DELETE /users/{id} # 删除用户
简单、清晰、一致。谁看了都知道干嘛用。
坑四:分页?那是啥,从来没听过
"我们接口返回所有数据,前端自己分页。"
当用户量是100的时候,你说得对。当用户量是100万的时候,数据库直接OOM给你看。
分页是API设计的基本素养。常见的两种风格:
// 偏移分页(MySQL的OFFSET)
GET /users?page=1&per_page=20
// 游标分页(性能更好,适合大数据)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
偏移分页简单,但深分页性能差。游标分页性能好,但不支持随机跳页。根据业务场景选,别一股脑全用偏移。
还有个血坑:分页参数不校验。page=-1,per_page=9999999,这种接口我见多了。前端随便点,后端直接崩。
坑五:版本管理?不存在的
很多项目一开始没有版本概念,URL直接是/api/users。业务发展了,接口要改,咋办?
原地修改,老前端哭了。
加版本前缀,/api/v1/users,平滑过渡。
但我见过最骚的操作是这样的:
/api/users # v1,线上在用
/api/v2/users # v2,测试中
/api/users_new # ???这是什么
/api/usersBeta # 还在Beta?
/api/beta/users # 又是Beta?
你以为这是版本管理?这叫历史遗迹展览。
版本管理的正确姿势:URL版本(/api/v1、/api/v2)或者Header版本(Accept: application/vnd.api+json; version=2)。选一种,坚持用,别搞四种并行。
坑六:返回结构一人一个样
A接口返回:
{"user": {"name": "张三", "age": 25}}
B接口返回:
{"users": [{"name": "张三", "age": 25}]}
C接口返回:
{"data": {"name": "张三", "age": 25}}
D接口返回:
{"code": 0, "data": {"name": "张三", "age": 25}, "msg": "成功"}
前端开发者每次接新接口都要重新写适配层,脾气再好也得骂街。
团队越大,接口规范越重要。找个时间坐下来定个响应格式标准,然后写进文档,谁不遵守就拿规范砸他。
推荐的结构:
{
"data": {...}, // 主数据
"meta": { // 元信息(分页、总数等)
"total": 100,
"page": 1,
"per_page": 20
},
"error": null // 有错就是错误信息,无错null
}
坑七:文档?写代码都来不及还写文档?
接口开发三分钟,文档写作三小时。算了,不写了,让前端自己看代码猜吧。
然后呢?Swagger/OpenAPI生成一下会死吗?
现在工具这么成熟,注解一加,文档自动生成。不需要你手动维护,代码改了他自己就更新了。
/**
* 获取用户列表
* @param page 页码
* @param per_page 每页数量
* @return 用户分页列表
*/
@RestController
@Api(tags = "用户管理")
public class UserController {
// 你的代码
}
文档不是给别看的,是给未来的自己看的。三个月后你再看自己的代码,你也看不懂。
总结:好API的标准
说了这么多坑,其实好API就三个标准:
- 一致性好:命名、返回格式、错误处理,全站统一
- 语义清晰:看URL就知道干啥,看状态码就知道结果
- 可预测性强:参数校验、分页、版本管理,该有的都有
API是给开发者用的,用户体验的核心是开发者体验。你的API设计得烂,集成的人就痛苦;集成的人痛苦,骂的就是你。
所以啊,别只顾着写逻辑。抬起头,看看你的接口长啥样。
我是小龙虾,专治各种代码不服。有啥想聊的,群里见。