前言
干了八年后端,见过的API设计没有一千也有八百。说实话,大多数都是能用就行的产物——没人review,没人规范,上来就RESTful一把梭,结果写出来的接口连自己第二天都看不懂。
今天不整虚的,就聊聊那些真实存在的、让人头疼的API设计毛病,以及怎么绕过去。不收门票,但看完你可能会想给我发红包。
问题一:HTTP方法乱用,POST干一切
这是我见过最多的骚操作:
// 查用户,用POST
POST /getUser
{ "id": 123 }
// 删除用户,用POST
POST /deleteUser
{ "id": 456 }
// 更新用户,还是POST
POST /updateUser
{ "id": 789, "name": "张三" }
兄弟,你这不叫RESTful,你这叫把POST当垃圾桶,什么垃圾都往里扔。HTTP定义了GET、POST、PUT、PATCH、DELETE这么多方法,不是给你看着玩的。
正确姿势:
GET /users/123 // 查询
PUT /users/456 // 完整更新
PATCH /users/789 // 部分更新
DELETE /users/111 // 删除
用对方法,接口语义清晰一倍,调试的时候你真的会谢我。
问题二:命名放飞自我,鬼知道这是什么
看看这些真实案例(已脱敏):
/api/getUserInfoByIdAndName
/api/user_query_for_pc_and_mobile
/api/getData(没有参数,不知道查什么)
/api/v1/api_v2_wrapper
我就想问:写这些接口的时候,脑子和手指是断开的吗?
API命名规范其实就三条:
- 名词优先,不要动词。写/users不要写getUsers
- 保持一致,同一个项目里不要一会儿用驼峰一会儿用下划线
- 有层次,资源嵌套不要超过两层,比如/users/123/orders可以,/users/123/orders/456/items/789就过分了
问题三:状态码返回一片200走天下
很多项目的响应是这个样子的:
// 请求成功了,返回200
{ "code": 404, "message": "用户不存在", "data": null }
// 请求失败了,还是返回200
{ "code": 500, "message": "服务器爆炸了", "data": null }
好家伙,HTTP状态码归你摆设的是吧?前端开发看到200,心里想的是"稳了",然后一解析code字段,发现是500。这种设计,我愿称之为薛定谔的成功——你不知道它到底成功没成功,只有打开code字段才知道。
标准姿势:
// 成功
HTTP 200 OK
{ "data": { ... } }
// 创建成功
HTTP 201 Created
{ "data": { "id": 123 } }
// 参数错误
HTTP 400 Bad Request
{ "error": "id不能为空" }
// 资源不存在
HTTP 404 Not Found
{ "error": "用户不存在" }
// 服务器爆炸
HTTP 500 Internal Server Error
{ "error": "系统异常" }
把状态码用起来,这是HTTP协议给你免费提供的语义层,不要白不要。
问题四:分页返回格式五花八门
这个问题在国内项目里尤其严重,同样是分页,返回格式能给你整出七八种:
// 方案A:把所有数据塞一个字段里
{ "data": [...], "count": 1000 }
// 方案B:放到meta里
{ "meta": { "total": 1000, "page": 1, "page_size": 20 }, "data": [...] }
// 方案C:放到pagination里
{ "pagination": { "total": 1000 }, "result": [...] }
// 方案D:根级平铺
{ "total": 1000, "page": 1, "page_size": 20, "list": [...] }
每接一个项目就得重新记一套分页格式,这是练记忆力呢?
推荐一个最常见的方案,GraphQL和很多大厂都在用:
{
"data": [...],
"page": {
"page": 1,
"page_size": 20,
"total": 1000,
"total_pages": 50
}
}
统一、明确、可扩展,前端看了不骂人。
问题五:不做版本控制,升级全靠祈祷
很多项目一开始没有版本概念:
/api/users
/api/orders
然后某天产品说:我要改返回结构,加个字段。
你一改,正在用的老客户端全炸了。噩梦开始。
正确的做法是在URL里预留版本:
/api/v1/users // 初始版本
/api/v2/users // 改动了结构,加字段
新版本来了,老版本还能跑一段时间,给客户端留足迁移窗口。这不是矫情,是工程上的基本尊重。
问题六:错误信息等于没有信息
这个真的太常见了:
// 错误
{ "error": "操作失败" }
// 正确
{
"error": {
"code": "VALIDATION_FAILED",
"message": "请求参数校验失败",
"details": [
{ "field": "email", "message": "邮箱格式不正确" },
{ "field": "age", "message": "年龄必须大于0" }
]
}
}
前者让前端开发对着屏幕发呆,后者让debug时间从两小时变成十分钟。高下立判。
总结:好API的标准其实很朴素
写了这么多,其实好API的核心就三点:
- 语义清晰:看到接口名就知道干什么,看到返回结构就知道是什么
- 一致性强:整个项目用同一套规范,不玩花活
- 对下游友好:错误信息要具体,分页要有章法,版本要有规划
API是给机器用的,但首先是给人看的。你写的每一行接口定义,都有可能在某个深夜被一个愤怒的前端开发盯着骂。
所以,写接口的时候,换位思考一下:你希望接别人写的API吗?
己所不欲,勿施于人。祝大家的接口永远不返工。
有问题?或者你们项目里还有什么奇葩接口设计?评论区见。