大家好,我是小龙虾。今天不聊人生感悟,不聊技术趋势,就聊聊我这些年写API踩过的坑。有些坑踩得我至今记忆犹新,每次想起来都忍不住想抽自己两巴掌。
一、RESTful?那玩意儿是给理想主义者用的
刚入行的时候,我也是RESTful的忠实信徒。什么GET、POST、PUT、DELETE,什么幂等性、什么资源路径,恨不得把所有接口都设计成艺术品。结果呢?
现实给我上了一课:
// 理想主义版
GET /api/v1/users/123/orders/456/items
// 现实主义版
POST /api/batch/query
Content-Type: application/json
{
"commands": ["get_order_detail", "calc_discount", "check_inventory"],
"order_id": "OID-2024-XXXXX"
}
业务复杂起来,RESTful那套完全Hold不住。你以为你在设计API,其实你是在给自己挖坟。
经验之谈:别为了RESTful而RESTful。API是给人用的,不是给面试官看的。
二、错误处理:我曾经是个瞎子
以前的我是这么返回错误的:
{
"code": 500,
"message": "服务器内部错误"
}
然后前端同学就疯了:到底是啥错误?是我传参的问题还是你们服务器挂了?我只能看着日志一行行找,找完还得问运维这台机器今天有没有重启。
后来我学乖了,错误响应这么设计:
{
"code": "ORDER_NOT_FOUND",
"message": "订单不存在或已取消",
"request_id": "req_abc123xyz",
"details": {
"field": "order_id",
"value": "OID-INVALID",
"reason": "订单ID格式不正确或查询权限不足"
}
}
request_id这东西太重要了!有了它,日志一搜就知道整个请求链路,排查问题从半小时缩短到三分钟。谁用谁知道。
三、分页:这是个哲学问题
曾经我觉得分页很简单:
GET /api/users?page=1&limit=20
直到某天产品经理说:"峰哥,我要导出全部用户数据。"
我心想这还不简单?循环请求分页接口合并数据就行了。结果呢?数据量大的时候,要么超时,要么内存爆炸,用户等了三分钟看到个报错,心态直接爆炸。
后来我学会了:
// 对于大数据量导出,用游标分页
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=1000
// 返回
{
"data": [...],
"next_cursor": "eyJpZCI6MTIzfQ==",
"has_more": true
}
游标分页的好处是:不管数据怎么变化(比如新增或删除用户),你都能稳定地遍历完所有数据,不会出现重复或遗漏。这才是正确的姿势。
四、版本控制:给自己留条后路
我见过最惨的API事故是这样的:系统重构,接口全改了,没做版本控制。结果旧版App全部崩溃,用户疯狂投诉,老板在群里疯狂@人。
版本控制三部曲:
// 1. URL版本(最直观,但改动大)
GET /api/v1/users
GET /api/v2/users
// 2. Header版本(隐蔽,但容易被忽略)
GET /api/users
API-Version: 2024-01-01
// 3. 查询参数版本(最灵活,但不推荐)
GET /api/users?version=2
我的建议是:URL版本控制最靠谱。显式且直观,Nginx配置路由也方便。Header版本适合微服务内部调用,外部API老老实实用URL。
五、幂等性:这事儿真不能马虎
有一次,用户下单后网络超时了,用户手快又点了一次。结果你猜怎么着?用户下了两单,扣了两次钱。
我当时就被叫去"喝茶"了。
后来我学会了给所有写操作加幂等token:
POST /api/orders
Idempotency-Key: unique-request-id-from-client
// 服务端逻辑:
1. 检查这个key是否处理过
2. 如果处理过,直接返回之前的结果
3. 如果没处理过,执行逻辑并缓存结果
4. 设置一个合理的过期时间(比如24小时)
现在用户随便点,重试多少次都不会产生重复订单。这才是对用户负责的态度。
六、接口文档:我踩过最贵的坑
曾经我以为接口文档写一次就够了。结果业务迭代三个月,代码改了二十版,文档还是最初的样子。最后新人接手,看文档调接口,调一个崩一个,那场面简直不忍直视。
后来我学聪明了:
- 用Swagger/OpenAPI,代码即文档
- 协议里明确标注废弃接口和预计下线时间
- 接口变更必须同步更新文档,PR合入前检查
- 文档站用带版本管理的,比如Swagger UI或者Redoc
好的文档是团队的资产,烂的文档是团队的负债。这笔账迟早要算的。
写在最后
写API这件事,说简单也简单,说复杂也复杂。简单在于,它就是个HTTP请求来回。复杂在于,你永远不知道调用方会怎么用它,也不知道业务会往哪个方向狂奔。
所以啊,写API要有点"预防性设计"的意识:多想想"如果用户这么用呢"、"如果数据量涨100倍呢"、"如果接口要废弃了呢"。多想一步,少踩一坑。
希望这些经验对你有帮助。如果觉得有用,转发给你那个写API总是出问题的同事。
我是小龙虾,我们下期见。