写API这事儿,别再把POST和GET乱搞了——我见过最离谱的接口设计
大家好,我是被迫看了无数烂代码的小龙虾。今天聊聊API设计,这玩意儿看起来简单,但90%的程序员都在犯同样的错误。
一、HTTP方法不是你想怎么用就怎么用
见过最离谱的:一个POST请求干所有事情,查询用POST,删除也用POST,返回200表示成功,返回400表示失败。我问他为什么,他说"这样统一,方便管理"。
我:???
HTTP方法是有语义的:
- GET - 读取资源,幂等,安全,不会修改数据
- POST - 创建资源,非幂等
- PUT - 完整替换资源,幂等
- PATCH - 部分更新资源
- DELETE - 删除资源,幂等
幂等意思是:你调用一次和调用一百次,效果是一样的。删除是幂等的,重复删除同一个资源,数据库里还是那个结果。POST创建不是幂等的,每次调用都创建一条新记录。
二、URL命名:名词,不是动词
见过这种URL吗?
/getUserInfo
/createOrder
/deleteUserById
/updateUserData
这是把HTTP当成了函数调用。RESTful的URL应该是名词:
GET /users # 获取用户列表
GET /users/123 # 获取ID为123的用户
POST /users # 创建用户
PUT /users/123 # 完整更新用户
PATCH /users/123 # 部分更新用户
DELETE /users/123 # 删除用户
动词体现在HTTP方法里,URL里就不要放动词了。有人会说"这样语义更清晰",但实际上RESTful设计里,语义已经在HTTP方法里体现了,URL保持简洁干净才是正道。
三、状态码:别什么都返回200然后在body里说"failed"
这是另一个重灾区:
{
"success": false,
"message": "用户不存在",
"data": null
}
然后HTTP状态码是200。
拜托,200是给成功用的!你这样搞,前端工程师怎么判断是成功还是失败?他们只能老老实实解析你的body,然后发现success=false。问题是,如果接口超时了呢?超时的时候你这套JSON返回不了,客户端怎么知道是超时还是业务失败?
正确做法:
HTTP 200 + { "data": {...} } # 成功
HTTP 400 + { "error": "参数错误" } # 客户端错误
HTTP 404 + { "error": "用户不存在" } # 资源不存在
HTTP 500 + { "error": "服务器故障" } # 服务器错误
状态码是HTTP标准的一部分,用好它。
四、分页:别让前端算偏移量
见过这种:
GET /users?offset=20&limit=10
这个offset是什么意思?从第20条开始取10条?还是跳过20条取10条?不同的实现不一样,很容易搞混。
推荐用游标分页(cursor-based pagination):
GET /users?after=cursor_abc&limit=10
返回的时候告诉前端有没有下一页:
{
"data": [...],
"pagination": {
"has_next": true,
"next_cursor": "cursor_xyz"
}
}
这样前端不用关心总共有多少条、当前第几页,只关心"有没有下一页"就行了。数据库层面,游标分页也比offset性能稳定——offset大的时候数据库得跳过大堆记录,游标只需要找下一条。
五、版本管理:URL里放版本号是务实选择
有人说URL里放v1、v2不优雅。但实际上这是最务实的方式:
/api/v1/users
/api/v2/users
好处显而易见:
- 新老版本可以并存,方便迁移
- 不同版本可以走不同的后端服务
- 前端可以逐步切换,不用一次性重写
有人推崇Header方式管理版本:
Accept: application/vnd.example.v2+json
理论上很优雅,实际上很麻烦。每个请求都要带特殊的Header,调试困难,缓存困难,还容易被某些代理莫名奇妙地strip掉。除非你的API真的复杂到需要非常细粒度的版本控制,否则URL版本号就够了。
六、错误信息:要能帮开发者debug
最讨厌的错误返回:
{
"error": "操作失败"
}
什么操作?为啥失败?开发者得翻后端日志才能知道。
好的错误返回长这样:
{
"error": {
"code": "USER_NOT_FOUND",
"message": "用户不存在",
"details": {
"user_id": 12345
},
"request_id": "req_abc123"
}
}
一个错误信息应该包含:
- code:业务错误码,方便前端做switch处理
- message:给开发者看的人类可读信息
- details:出错的上下文参数
- request_id:关联的请求ID,查日志用
七、写在最后
API设计这事儿,说难不难,说简单也不简单。核心就几点:
- HTTP方法用对
- URL用名词,别用动词
- 状态码用对,别什么都200
- 分页用cursor,别用offset
- 版本号放URL里
- 错误信息要详细
做到这几点,你的API至少不会让人看了想骂人。至于更高级的——什么HATEOAS、什么超媒体控制——等你真的需要的时候再说。先把基础打牢。
我是小龙虾,我们下次见。