做后端开发这么多年,我见过太多「能跑就行」的API。它们活着,但活得憋屈——调用方骂骂咧咧,文档写得像谜语,出了问题互相甩锅。
今天不整虚的,就聊几个我踩过坑、交过学费才悟出来的API设计道理。
一、URL不是文件路径,别把它当文件系统
见过这种API吗?
GET /api/v1/getUserByIdAndReturnBasicInfo
GET /api/v2/user/get_user_info_v2_final
兄弟,你这是在写代码还是写遗书?URL是资源的地址,不是函数的文件名。RESTful的核心就一句话:用名词,不用动词。
好的API应该是这样的:
GET /api/users/123 # 获取用户
PUT /api/users/123 # 更新用户
DELETE /api/users/123 # 删除用户
POST /api/users # 创建用户
简洁、清晰、一眼看懂。有人会说:「你这不是RESTful教条主义吗?」
我的回答是:不是教条,是对调用你API的人最基本的尊重。人家半夜debug的时候,看到这种干净的URL,心里能不给你磕一个?
二、状态码不是装饰品,是工程师之间的暗号
你要是见到有人200返回「用户不存在」,400返回「服务器内部错误」,那这位朋友可能把HTTP状态码当成了调色盘——随便涂。
状态码是API和调用方之间的暗号,说错了话,整个链路都要乱套。
我的经验:
- 200 - 成功,且业务逻辑也正常
- 201 - 创建资源成功(POST有效)
- 400 - 请求参数有问题,别废话,直接告诉人家哪里错了
- 401 - 没登录或token过期
- 403 - 登录了但没权限
- 404 - 资源不存在,别装
- 500 - 服务端抽风了,但至少承认了
最烦的一种设计是:无论发生什么,永远返回200,然后在body里塞一个code字段说「操作失败」。这是把调用方当傻子——人家还得先解析body才能知道到底发生了什么。
别这样。HTTP协议给了你这么多状态码,用起来。
三、错误信息要像个正常人一样说话
见过这种错误返回吗?
{
"code": -1,
"msg": "操作失败"
}
「操作失败」——废话,我当然知道操作失败了,我要是知道哪里失败了还用来查你这个API?
好的错误返回应该长这样:
{
"code": 10001,
"message": "手机号格式不正确,请输入11位数字,当前输入:1381234",
"field": "phone",
"request_id": "abc123"
}
看,这才叫有效沟通。告诉人家:
- 什么问题
- 哪个字段
- 当前输入是什么
- 有个RequestID方便查日志
我知道写这种详细的错误信息很烦,但你要想到:调用你API的人,可能正在被领导催着上线,正在被用户骂,正在凌晨三点debug。你的API多写一行说明,可能就救了一个人。
四、分页不是可选的,是必须品
如果你写一个查询用户的API,返回全部数据——恭喜你,要么数据库被拖垮,要么调用方内存爆掉,要么两个一起爆。
分页是后端开发的基本素养,不接受反驳。
GET /api/users?page=1&page_size=20
返回的时候,告诉人家总数和总页数:
{
"data": [...],
"pagination": {
"total": 1024,
"page": 1,
"page_size": 20,
"total_pages": 52
}
}
有人会说:「我用游标分页不行吗?」行,当然行。但对于大多数业务场景,传统的页码分页足够简单、足够好用。别为了「高级」而「高级」。
五、版本号不是版本控制,是保险
你的API不可能一成不变。业务在变,需求在变,坑在填——API总要升级。但升级之后,老的调用方怎么办?
答案是:版本号。
/api/v1/users
/api/v2/users
当你的API要做不兼容的改动时,新开一个版本,让老版本再活一段时间。这样:
- 调用方有时间迁移
- 你不用在生产环境里两边同时伺候
- 万一新版本有bug,还能快速回滚
我见过一个项目,API从v1直接跳到v5,问他v2-v4去哪了,答曰:「改了太多次,直接跳版本号了。」——这是懒,不是聪明。
写在最后
API设计这种事,说难听点,是「费力不讨好」的活。你花三天设计的优雅接口,不如别人两小时写的糙代码上线快。
但我想说:代码是给机器跑的,但API是给人用的。这个「人」,可能是别的工程师,可能是三周后的你自己,可能是一个被产品经理催得焦头烂额的同事。
做一个让人用起来舒服的API,其实就是在给未来的自己积德。
共勉。