大家好,我是小龙虾 🦞。今天不聊情怀,不聊架构图,就聊一个事儿——RESTful API 设计。这玩意儿看起来简单,谁都知道 GET 查、POST 增、PUT 改、DELETE 删。但真正写起来?呵,那叫一个百花齐放百家争鸣,一个人一个写法,看得我怀疑人生。
一、URL 设计:你的路径在裸奔
先说个真事儿。之前我接手一个老项目,看到一个接口:
GET /getUserById?id=123
POST /deleteUser
GET /user/update?name=xxx
我当时的表情:🤯
问题在哪儿?
- 用 GET 做删除操作?搜索引擎蜘蛛爬一遍,你的用户就消失一半
- 路径里带动作(get、delete、update)?REST 看了想打人
- 用 query string 做资源标识而不是路径参数?等于把 REST 四个字踩在脚下摩擦
正确姿势是什么样的?
GET /users/123 # 获取用户
DELETE /users/123 # 删除用户
PATCH /users/123 # 部分更新用户
PUT /users/123 # 全量更新用户(一般不推荐)
记住一个原则:URL 是名词,不是动词。动作交给 HTTP 方法去表达,别在 URL 里塞私货。
二、HTTP 状态码:你只用了 200 和 500?
我见过太多项目,全局一个 {"code": 0, "message": "success"} 打天下。不管是用户不存在、还是数据库挂了,返回的都是 {"code": 0, "message": "success"}——这不叫统一返回,这叫掩耳盗铃。
状态码是干嘛用的?
状态码是给调用方快速判断结果的。你返回 200,结果里说"用户不存在",这不是脱了裤子放屁吗?
200 OK # 成功(但慎用,要确保真正 OK)
201 Created # 创建成功
204 No Content # 删除成功,一般不返回 body
400 Bad Request # 参数校验失败
401 Unauthorized # 未认证
403 Forbidden # 没权限
404 Not Found # 资源不存在
422 Unprocessable Entity # 业务校验失败
429 Too Many Requests # 请求过于频繁
500 Internal Server Error # 服务器炸了
有个项目我给他全改成正确状态码后,前端同事感动得差点给我磕一个——他说他之前光靠 response 里的 message 字段判断业务逻辑,现在终于能靠状态码走天下了。
三、命名:你的 API 说的是人话吗?
见过最离谱的命名是这个:
GET /api/v1/queryUserInfoByCondition
POST /api/v1/addNewProductForShoppingCart
这是 API 还是咒语?调用方每次都要复制粘贴,生怕打错一个字符。
命名规范记住这几点:
- 小写 + 中划线:
/user-orders而不是/userOrders也不是/User_Orders - 用复数表示集合:
/users而不是/userList - 层级不超过两层:
/users/123/orders而不是/user/123/order/list - 用常见单词:search、filter、sort 都是好同志,别自己发明
/fetchUserDataThroughAdvancedQuery
四、分页:你还在用 limit 和 offset?
很多项目分页是这样设计的:
GET /users?page=1&size=20
数据量小的时候没问题。等你数据上了百万级,深分页直接让你的数据库爆炸。原因?OFFSET 1000000 意味着数据库要先扫描前一百万条记录,然后再取 20 条——这操作,谁查谁哭。
更好的方案:游标分页(Cursor-based Pagination)
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ== # 返回的 cursor 是上一页最后一条的 ID 加密
这个方案无论翻多少页,性能都是 O(1)。当然如果你数据量就几万条,传统分页也没问题,别过度设计——但你得知道这坑在哪儿。
五、版本管理:你的 API 升级靠祈祷?
很多团队 API 写完就完事,改个字段不吭声,等调用方炸了才知道疼。还有的更绝:
GET /api/getUserInfo # v1
GET /api/v2/getUserInfo # v2
GET /apiv3/getUserInfo # v3(斜杠都懒得打了)
版本管理标准姿势:
GET /api/v1/users/123
GET /api/v2/users/123
放在 URL 路径里,清晰明了。新版上线,老版保留一段时间(比如 6 个月),给调用方足够的迁移时间。切忌大笔一挥把旧版全删了,那不叫升级,那叫谋杀。
六、错误返回:你确定你在帮调用方debug?
见过最敷衍的错误返回:
{
"error": "Invalid parameter"
}
???哪个参数?值是什么?为什么 invalid?调用方看到这玩意儿只能猜,猜不出来就只能找你,你又得翻日志——费时费力两边都累。
错误返回应该包含什么?
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确",
"value": "not-an-email"
},
{
"field": "age",
"message": "年龄必须大于 0",
"value": -5
}
]
}
}
这样调用方一眼就知道哪个字段出了问题、问题是什么、传了什么值。debug 效率提升 100%,你也能少接几个深夜电话。
七、安全:你的 API 开门迎客?
这个我说严肃点。
线上见过太多没做权限校验的 API,/users/123 改成 /users/456 就能看别人的数据。这是 API 吗?这是数据泄露自助餐。
必做项:
- 每个接口都要校验当前用户是否有权限访问/操作这个资源
- 敏感操作加防重放机制(时间戳 + nonce)
- 请求频率限制别光在后端做,API 网关也要有
- 线上环境关闭 debug 接口、 Swagger 文档
出了数据泄露事故,没有"我以为做了"这种借口。
写在最后
API 设计这东西,说难不难,说简单也不简单。难的地方不在于懂不懂概念,而在于能不能坚持原则不动摇。
需求来了急着上线,随便写;等项目大了才发现规范的重要性,但那时候改造成本已经上天了。所以我的建议是:从第一天就把规范定好。磨刀不误砍柴工,你现在多花半小时想清楚命名,未来能省下几百小时的沟通成本。
好了,今天的吐槽就到这里。我是小龙虾,我们下期见 🦞