写API这事儿:那些年我们一起踩过的坑
做后端开发这么多年,我越来越觉得API设计有点像装修房子——你可以随便堆,但住进去之后每天都在后悔。不同的是,房子后悔了还能敲掉重来,线上API你敢动试试?分分钟被业务方骂到怀疑人生。
今天不整虚的,说点实战中真正管用的东西。全是血泪教训,拿出来给大家避避雷。
一、HTTP方法不是装饰品,别乱用
见过太多人把POST当万能钥匙用。查个用户信息,POST /getUser;删个订单,POST /deleteOrder。功能是能跑,但这种设计蠢得让人心疼。
HTTP语义是有一套标准的:
- GET — 查,就该只干这事
- POST — 增,创建一个新资源
- PUT — 改,全量替换
- PATCH — 改,局部更新
- DELETE — 删,别犹豫
有人会说:"我用POST都能搞定,省事。" 对,你省事了,调用方疯了。一个成熟的API,GET/PUT/PATCH/DELETE这些动词本身就是文档。看一眼接口就知道在干啥,这才叫好的设计。
// 好的设计
GET /users // 获取用户列表
POST /users // 创建用户
GET /users/123 // 获取单个用户
PUT /users/123 // 全量更新用户
PATCH /users/123 // 部分更新用户
DELETE /users/123 // 删除用户
// 灾难现场
POST /getUsers
POST /createUser
POST /getUserById
POST /updateUser
POST /deleteUser
二、状态码是给机器看的,但你得让人也能看懂
HTTP状态码是API和调用方交流的第一语言。但我发现很多后端程序员的状态码使用逻辑是这样的:成功就200,失败就500,剩下的随缘。
来,复习一下该有的态度:
- 200 — 成功,没毛病
- 201 — 创建成功,资源诞生了
- 204 — 成功但没内容,DELETE常用
- 400 — 客户端的错,参数不对、格式有误
- 401 — 未认证,没登录
- 403 — 已认证但没权限
- 404 — 资源不存在
- 409 — 冲突,比如重复创建
- 422 — 请求格式对,但语义有问题
- 429 — 请求太多了,限流
- 500 — 服务端炸了,但不是你的错
关键是:错误响应体里一定要带code字段和message字段。code是给程序判断用的,message是给人看的。线上出问题了,人家一看message就知道怎么回事,比你写一百行日志都管用。
{
"code": "USER_NOT_FOUND",
"message": "用户不存在或已被删除",
"requestId": "abc123"
}
三、分页不是可选项,是必选项
如果你设计一个列表接口不加分页,等用户数据量上了十万级别,你就知道什么叫午夜凶铃了。
分页方案有两种主流风格:
Offset式:
GET /users?page=1&pageSize=20
简单直观,但有性能问题。数据库OFFSET越大越慢,深度分页会要命。
Cursor式(游标分页):
GET /users?cursor=eyJpZCI6MTIzfQ&pageSize=20
用最后一条的ID做锚点,性能稳定,不管翻到第几页速度都一样。缺点是没法跳页。取舍看你场景,列表类推荐游标分页。
四、版本控制这事儿,越早做越轻松
初期设计API的时候,所有人都觉得接口稳定、不需要版本。等业务一上线,需求一变,你就知道当初有多天真。
版本号放URL里是最清晰的:
/api/v1/users
/api/v2/users
有人喜欢用Header做版本,我理解,但URL版本更直观。调用方能看到、用到、改到,心里有底。
版本迭代的时候遵循一个原则:老版本尽量撑久一点。每次升级都是对调用方的入侵,人家要改代码、要测试、要发版。你多撑三个月,可能就救了人家一命。
五、幂等性:网络不稳时的救命稻草
想象一下这个场景:客户端发了个创建订单的请求,结果网络超时了。客户端重试,结果创建了两个订单。客诉电话打爆,你的KPI也爆了。
这就是幂等性的意义——同一个请求执行一次和执行多次,效果是一样的。
实现方式:在请求里加一个幂等Key(也叫请求ID),服务端把这个Key和业务ID关联起来。如果这个Key已经处理过,直接返回之前的结果。
POST /orders
Headers: {
"Idempotency-Key": "unique-request-id-12345"
}
Body: {
"userId": 100,
"amount": 299.00
}
POST、PUT、PATCH、DELETE这些写操作都应该考虑幂等性。GET是天然幂等的,这个不用担心。
六、写在最后
API设计没有银弹,但有坑可以避开。把HTTP语义用对、把状态码返回准、把分页加上、把版本控制好、把幂等性考虑进去——做到这几点,你的API至少不会让人想砸键盘。
好的API设计就像好的代码注释,平时不觉得有用,等到别人接手的时候、等到线上出故障的时候、等到半夜被叫起来的时候,你就会感激当初那个认真写接口的自己。
共勉。