做后端开发这么多年,听过最多的抱怨就是:「这API谁设计的,脑子进水了吧?」然后一问,return的是200但业务失败了,参数命名全是拼音缩写,文档写的是「参数见下方」然后下方什么都没有。
今天不整虚的,直接聊REST API设计中那些容易翻车但又没人正经教的东西。都是我踩过的坑,看完能少挨骂。
状态码不是随便写的
很多人以为HTTP状态码就是个装饰品,200万能,出了事先返回200再说。这种做法跟考试作弊交白卷然后在卷子画个笑脸一样——老师看了想打人。
说几个最常见的场景:
- 创建资源成功 → 201 Created,顺便把新资源的URL放Location头里
- 删除成功但本来就不存在 → 204 No Content,别返回404因为你确实「处理了」请求
- 请求格式对了但业务逻辑不对 → 422 Unprocessable Entity,别塞进200里假装天下太平
- 认证失败 → 401,没权限是403,分清楚
基本原则:能用状态码说清楚的事,别用返回体里的code字段绕弯子。你多写一个字段,前端就要多判一个字段,烦不烦啊。
分页:这是个考验人品的地方
分页看似简单,但90%的实现都有问题。最经典的:「page=1&size=20」然后告诉你总共有999条,这是让人算总页数玩呢?
推荐用cursor-based pagination(游标分页),理由:
// 请求
GET /api/v1/articles?cursor=eyJpZCI6MTIzfQ&limit=20
// 返回
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTQzfQ",
"has_more": true
}
}
好处是什么?数据动态变化时不会重复或跳过。比如你翻到第三页,中间有人新插入了一条记录,offset分页会让你要么看到重复的,要么跳过的。cursor分页不存在这个问题。
什么时候用offset分页?后台管理列表需要跳页的场景可以用,但得同时返回total_count让人知道总共有多少页。
字段命名:保持一致就是最好的文档
这个问题我见过最离谱的是:一个项目里同时出现 created_at、createTime、create_at、add_time 四种写法,问就是「历史遗留问题」。
定个标准就完事了,推荐遵循JSON API规范:
- 蛇形命名:create_at、article_title
- 时间戳统一用毫秒级Unix时间戳,别用字符串日期
- 布尔值加is/has/can前缀:is_active、has_permission、can_edit
还有一个很多人忽略的:分清input和output的字段名。比如创建文章的API,接收的是title和content,但返回的要包含id、created_at、author这些服务端生成的东西。别让前端去猜你返回了啥字段。
错误处理:说人话
错误返回里最让人想砸键盘的是这种:
{
"code": -1,
"msg": "操作失败",
"data": null
}
}
「操作失败」——废话,我知道操作失败了,我问的是为啥失败。这种错误返回等于没返回。
好的错误结构应该是这样:
{
"error": {
"code": "ARTICLE_NOT_FOUND",
"message": "请求的文章不存在或已被删除",
"details": {
"article_id": 12345
}
}
}
}
code用业务层面的错误码,方便前端做switch-case判断;message是给人看的,说清楚什么情况;details是技术上下文,帮助排查问题。
还有个关键点:不要暴露内部实现细节到错误信息里。你数据库连接失败了,错误信息写「SQL执行失败: Connection refused」是方便黑客还是方便调试?对外只说「服务暂时不可用」,日志里记详细的信息就行了。
版本管理:别搞成考古现场
最常见的两种版本策略:URL里写版本号(/api/v1/),或者用Header(Accept: application/vnd.api+json; version=1)。
我的建议是:用URL版本,简单直接。理由:
- 调试方便,直接复制URL就能分享给别人
- CDN和网关更容易处理
- 所有HTTP工具原生支持,不需要特殊Header配置
版本升级的节奏:不要轻易破坏性变更。加了新字段算小事儿,换了必填参数或者改了返回结构这种,才是让前端原地爆炸的操作。非改不可的话,先把新版本跑一段时间,等旧版本流量清零了再下掉。
写在最后
API设计这事,说难听点就是「别人骂你的时候才知道做得好不好」。所以别闭门造车,写完了让前端过来挑刺,听取意见,迭代几轮自然就好了。
当然,最好还是别等人骂了再做对。
题图:Photo by Annie Spratt on Unsplash