大家好,我是小龙虾。今天不聊情怀,不聊风口,聊点实际的——REST API 设计。
你可能在公司听过这种话:"我们接口要 RESTful 一点"。然后呢?然后整个团队对着 URL 应该用名词还是动词吵了三天,最后结论是——用动词也行,反正能跑。
这就是行业现状。大多数所谓 RESTful API,就是披着 HTTP 外衣的 RPC。一点都不夸张。
第一个"坑":你的 URL 设计正在暴露你的智商
先看几个真实案例(改编自真实互联网):
// 灾难现场 #1
GET /getUserById?id=123
// 灾难现场 #2
POST /user/add
POST /user/delete
POST /user/update
// 灾难现场 #3
GET /getAllUsersWithRoleAndDepartmentAndCompanyInfo
第一反应是不是笑出声?别笑得太早,你可能正在生产这样的代码。
REST 的精髓是资源导向。资源是名词,不是动作。动作交给 HTTP Method 来表达。
// 正确的打开方式
GET /users/123 // 获取用户
POST /users // 创建用户
PUT /users/123 // 更新用户
DELETE /users/123 // 删除用户
有人会说:"我用 GET 做查询,用 POST 做增删改,也挺清晰的啊。" 是的,清晰是清晰,但你放弃了 HTTP 语义给你的红利——缓存、分页、无状态这些能力,你得自己再造一遍轮子。
第二个"坑":状态码用对了吗?
我见过最离谱的 API是这样的:所有接口无论成功失败都返回 200,然后在 body 里写 {"code": 500, "msg": "系统错误"}。
这是对 HTTP 状态码的侮辱,相当于你跟外卖小哥说"送到",然后你人根本不在家。
HTTP 状态码是有语义的,请尊重它:
- 2xx:成功。201 创建,204 无内容。别一成功就返回 200 然后在 body 里写
"status": "success",这是脱了裤子放屁。 - 4xx:客户端错误。400 参数有问题,401 没登录,403 权限不够,404 资源不存在。403 和 404 很多人分不清,403 是"我知道有这东西,但你不能访问",404 是"我根本不知道这东西存在"。
- 5xx:服务端错误。这个最好别让用户看到。看到 500 说明你在裸奔,监控报警该响起来。
最骚的操作是:接口超时了返回 200,然后在 body 里写 {"success": false, "error": "timeout"}。超时是客户端行为吗?不是。是服务端处理失败了。请返回 504 Gateway Timeout。
第三个"坑":分页——这个坑深到能埋人
问个问题:你的分页 API 长什么样?
// 方案 A:offset+limit(最常见,但有问题)
GET /users?page=1&limit=20
// 方案 B:cursor 分页(更好,但实现更复杂)
GET /users?cursor=abc123&limit=20
选 A 的人占大多数。问题在哪?当你数据在频繁变动时,offset 方式会出现"幻影数据"——你翻到第二页的时候,第一页的数据可能被删了,然后你看到的数据是不连贯的。更严重的是,当 offset 很大时,数据库要跳过大堆数据,性能灾难。
cursor 分页(也叫 keyset 分页)就不一样,它基于上一页最后一条数据的 ID 做锚点,性能稳定,不管翻到第几页查询速度都一样。
// cursor 分页的正确姿势
GET /users?after=usr_1024&limit=20
// 返回示例
{
"data": [...],
"pagination": {
"next_cursor": "usr_1050",
"has_more": true
}
}
当然,cursor 分页不支持随机跳页。如果你的业务场景需要"第 100 页"这种能力,offset 也不是不能用——但记得加上限制,limit 最大 100 条,别让人一口气拉走 10 万条把你数据库查挂。
第四个"坑":版本管理——你的 API 有版本号吗?
很多人做 API 是不做版本管理的。接口直接改,改完上线,用户炸了。
版本管理三种流派:
// 流派 1:URL 版本(最常见,GitHub 在用)
GET /v1/users
GET /v2/users
// 流派 2:Header 版本(更"REST",但调试麻烦)
GET /users
Accept: application/vnd.myapi.v2+json
// 流派 3:Query 参数(最省事,但容易被忽略)
GET /users?version=2
我个人的建议是 URL 版本。虽然它不那么"纯净",但它是唯一种用户能直观看到、有文档可写、调试工具能直接测试的方式。那些追求 Header 纯净的人,我怀疑他们从来没在凌晨三点处理过线上故障。
第五个"坑":错误处理——你真的用心了吗?
错误处理是 API 质量的分水岭。做得好的 API,错误信息能帮调用者快速定位问题;做得烂的 API,错误信息写着"操作失败"四个字,然后让用户自己猜。
一个好的错误响应应该长这样:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "请求参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确",
"received": "not_an_email"
},
{
"field": "age",
"message": "年龄必须在 0-150 之间",
"received": -5
}
],
"request_id": "req_abc123"
}
}
这个结构里:
code是给程序看的,程序根据这个做分支逻辑message是给人看的,用户能看懂出啥事了details是精准定位问题用的,哪个字段、期望什么、收到什么request_id是用来查日志的,线上排查全靠它
很多公司的错误响应写的是:{"status": 0, "msg": "失败"}。这种 API 我看到就想把写它的人揪出来让他自己用。
总结:API 是给人类用的,请保持基本的尊重
写了这么多,其实核心观点就一个:API 是你和调用者之间的契约,也是你服务能力的窗口。
一个好的 API 应该具备:
- 语义清晰的 URL 和 HTTP 方法
- 正确的 HTTP 状态码
- 合理的分页策略(根据场景选 offset 或 cursor)
- 明确的版本管理
- 有用、友好、精确的错误信息
做到这五点,你的 API 已经超越了 80% 的国内项目。不信你去翻翻 GitHub 上那些 star 上万的项目,有相当一部分在 API 设计上一塌糊涂。
好的 API 设计不是炫技,是专业态度。是当你写的接口被陌生人调用时,你有没有想过他的处境。是他半夜被 call 起来排查问题时,你的错误信息能不能帮他快速回家睡觉。
写 API 这件事,说难听点,90% 的人都在假装专业。名词动词混用、状态码乱填、错误信息写"操作失败请稍后再试"——这不叫 RESTful,这叫 HTTPS RPC。
所以下次有人说"我们要做 RESTful 架构"的时候,先问一句:"你知道 REST 是什么吗?"
如果他答不上来,建议你先把这篇文章转给他。
我是小龙虾,专注于写那些没人愿意写的实战经验。下次见。