惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 三生石上(FineUI控件)
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
I
InfoQ
Latest news
Latest news
H
Help Net Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Exploit Database - CXSecurity.com
雷峰网
雷峰网
Know Your Adversary
Know Your Adversary
人人都是产品经理
人人都是产品经理
C
Cisco Blogs
NISL@THU
NISL@THU
P
Proofpoint News Feed
Y
Y Combinator Blog
I
Intezer
博客园_首页
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
MyScale Blog
MyScale Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
有赞技术团队
有赞技术团队
S
Schneier on Security
N
Netflix TechBlog - Medium
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Blog of Author Tim Ferriss
K
Kaspersky official blog
博客园 - 聂微东
S
Securelist
Recent Announcements
Recent Announcements
A
About on SuperTechFans
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
大猫的无限游戏
大猫的无限游戏
宝玉的分享
宝玉的分享
The GitHub Blog
The GitHub Blog
博客园 - 司徒正美
Vercel News
Vercel News
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
U
Unit 42
T
Tailwind CSS Blog
云风的 BLOG
云风的 BLOG
B
Blog
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More

希仁之拥

领克900半年使用体验 | 希仁之拥的博客 Ubuntu 26.04 Desktop使用体验 | 希仁之拥的博客 【转载】谈谈不受欢迎的博客技术特征 | 希仁之拥的博客 【转载】ClaudeCode 你想知道的所有秘密,源码深度研究报告 | 希仁之拥的博客 2025年年终总结 | 希仁之拥的博客 集成和使用Openclaw后的思考 | 希仁之拥的博客 我买了领克900 | 希仁之拥的博客 服务器性能优化之io拷贝 | 希仁之拥的博客 Go-Sail导航站上线啦 | 希仁之拥的博客 今年国庆的一些感受 [2025] | 希仁之拥的博客 在Deepin 25上配置forticlient | 希仁之拥的博客 分享一些酷酷的站点 [20250908] | 希仁之拥的博客 Go-Sail发布v3.0.6版本了 | 希仁之拥的博客 我对V2EX发布$V2EX讨论的一些感受 | 希仁之拥的博客 如何让Stripe支持支付宝和微信支付 | 希仁之拥的博客 2025上半年里程碑 | 希仁之拥的博客 GitLab+Drone使用体验 | 希仁之拥的博客 四姑娘山之旅 | 希仁之拥的博客 近来帮同事做性能优化的过程回顾 | 希仁之拥的博客 由GORM的Updates语法糖 我把 Go-Sail 的文档站更新了 | 希仁之拥的博客 这就是我为什么讨厌拼多多 | 希仁之拥的博客 元旦快乐~ | 希仁之拥的博客 致敬还在写博客的我们 | 希仁之拥的博客 逐步的把图片资源迁移到星光图床上 | 希仁之拥的博客 帮弟弟配了一台mini主机 | 希仁之拥的博客 国庆的一些碎碎念 | 希仁之拥的博客 就这一刻而言,我觉得科技冷冰冰的。 | 希仁之拥的博客 如何使用acme.sh自动续签证书 | 希仁之拥的博客 Go-Sail的文档站点搭建好了 | 希仁之拥的博客 我的第一个谷歌浏览器插件发布成功啦! | 希仁之拥的博客 我开源了一个golang的web框架 | 希仁之拥的博客 流芳 | 希仁之拥的博客 第一次开发微信小程序 | 希仁之拥的博客 春节快乐 | 希仁之拥的博客 我把博客站点重写了 | 希仁之拥的博客 阆中古城 | 希仁之拥的博客 入了把Keydous NJ80 | 希仁之拥的博客 Mac上有用的知识 | 希仁之拥的博客 记一次服务器被攻击 | 希仁之拥的博客 Ubuntu server不停机扩容系统盘 | 希仁之拥的博客 端午安康 | 希仁之拥的博客 搭建hadoop伪分布式集群总结 | 希仁之拥的博客 在goa框架中,如何访问原始请求数据 | 希仁之拥的博客 postgresql相关操作笔记 | 希仁之拥的博客 golang解析带命名空间的xml数据(带冒号的节点或属性名) | 希仁之拥的博客 【转载】解决deepin环境下wine QQ无法接收图片问题 | 希仁之拥的博客 杂项笔记 | 希仁之拥的博客 Mac如何修改通知横幅消失时间 | 希仁之拥的博客 Golang校验结构体是否实现了接口 | 希仁之拥的博客 关于deepin os的大半年使用体验 | 希仁之拥的博客 有态度的翻唱 | 希仁之拥的博客 【转载】Golang 在 Mac、Linux、Windows 下如何交叉编译 | 希仁之拥的博客 和幺幺一起成长的半年时光 | 希仁之拥的博客 Element UI中message消息提示实现原理 | 希仁之拥的博客 从predis组件开始了解如何与redis服务交互 | 希仁之拥的博客 【转载】 尤雨溪谈Vue.js :缔造自由与真我 | 希仁之拥的博客 【转载】百万 Go TCP 连接的思考: epoll方式减少资源占用 | 希仁之拥的博客 【分享】移除git中的commit历史(保留文件,仅移除提交历史信息) | 希仁之拥的博客 shell学习笔记 | 希仁之拥的博客 【分享】Ruby 之父松本行弘:程序员的乐趣、存在感与平衡 | 希仁之拥的博客 安利一下我用过的一些sublime text 3工具、插件及配置 | 希仁之拥的博客 PHP处理苹果内购数据验证 | 希仁之拥的博客 PHP获取XML所有节点名称 | 希仁之拥的博客 Sentinel::check() 在Laravel5.3版本之后总是返回false的解决方案 | 希仁之拥的博客 吃货的美食集,持续更新~ | 希仁之拥的博客 第二十一届成都汽车博览会 | 希仁之拥的博客 马云宣布1年后卸任集团董事局主席 由CEO张勇接任 | 希仁之拥的博客 nginx配置非443端口的https连接 | 希仁之拥的博客 PHP7 异常类型列表 | 希仁之拥的博客 【分享】《燃点》:不被嘲笑的愿望是不值得去实现的|一部属于创业者的电影 | 希仁之拥的博客 【分享】前端资源加载优先级[译] | 希仁之拥的博客 【小技巧】Laravel动态配置多个数据库连接 | 希仁之拥的博客 php使用ftp上传、下载文件 | 希仁之拥的博客 php使用mysqlnd扩展进行mysql异步并行查询 | 希仁之拥的博客 基于以太坊区块链,使用web3.js在geth服务器上执行合约方法以及事件监听 | 希仁之拥的博客 Smartisan OS | 希仁之拥的博客 母校 | 希仁之拥的博客 php后期静态绑定 | 希仁之拥的博客 [mysql优化]子查询与连接查询 | 希仁之拥的博客 PHP常见运行模式及原理 | 希仁之拥的博客 mysql用户与授权 | 希仁之拥的博客 如何在ubuntu14.04上安装composer | 希仁之拥的博客 关于mews/captcha for laravel存在method [validateCaptcha] doesn't exists解决方法 | 希仁之拥的博客
聊聊接口的返回数据结构 | 希仁之拥的博客
希仁之拥 · 2025-05-28 · via 希仁之拥

前段时间在推上看到好几篇讨论接口响应格式的推文,下面的评论有好多都在吐槽和抱怨返回值没有必要包含code码以及错误信息,因为http的code码和code信息已经足够用了,加上纯粹是属于脱了裤子放屁。今天我来和大家聊聊我的想法。

以下讨论的接口响应结构皆是基于博主开源的框架go-sail来进行的。

仓库地址: https://github.com/keepchen/go-sail

首先让我们来看一下go-sail的接口响应都有哪些字段:

{
  "code": 200,
  "data": null,
  "message": "SUCCESS",
  "requestId": "5686efa5-c747-4f63-8657-e6052f8181a9",
  "success": true,
  "ts": 1670899688591
}

除了data字段是interface类型(你可以理解为是其他语言的泛型)外,其余字段都是明确的数据类型。它们都有各自的含义和应用场景。

我们现在逐个解释一下字段的含义:

  • code
    业务码,int类型,可以对应Javascript语言的number类型,对应Java语言的int或Integer类型。

  • data
    数据体,业务的具体数据响应实体。该类型为Go语言的interface,可以对应其他语言的泛型。

  • message
    提示消息,string类型。

  • requestId
    请求唯一标识符,string类型。

  • success
    是否成功,bool类型。用于业务逻辑处理是否成功。

  • ts
    系统当前毫秒时间戳,int64类型,可对应Javascript语言的number类型,Java语言的Long类型。

code

有不少人都说,当请求成功时,http状态码已经是200了,这里的code码又写一遍200,纯粹就是脱了裤子放屁。这里需要解释一下:

1.go-sail提供的默认code值为0,在使用过程,开发者可以根据自己的需要自行指定成功的code为多少。
2.我建议成功的code码应该设置为非0值,原因是出于兜底方案的考虑,当你在某处调用遗忘了对code进行赋值,那么code=0的情况就与你设置的非0成功code码不相等,此时兴许能在关键时刻救你一命(KPI和BUG率警告⚠️)。

那么为什么非要设置业务code呢?

在回答这个问题之前,我想问一句,你有没有集成过阿里云、腾讯云、七牛云等等的云服务商的功能接口?有没有发现他们的接口都有业务code。这里提到他们并非人云亦云,他们有我就该照葫芦画瓢也要有。而是业务code有其必然存在的原因,否则大家的设计不会出奇的一致。

其他的设计者思路我不清楚,我谈谈我的设计思路:

  • 业务码可以尽可能详尽的表现业务逻辑处理过程中遇到的各种问题,表达更多的业务处理状态或是中断状态。
  • 业务码可以结合为前端或客户端提供更好的多语言支持。

先说第一点,就拿常见的购物下单逻辑来举例,code=1000可以表示余额不足,code=1001可以表示库存不足,code=1002可以表示在并发竞争的时候抢购失败,code=1003可以表示写入数据库的时候由于网络抖动而写入失败。等等等等。

当前端或客户端拿到对应的code码,可以根据前端的本地化配置,根据code码以及用户当前的系统语言,为用户展示出足够人性化的提示语,例如:“抱歉,当前商品库存不足”,“您当前的余额不足”,“您来晚了,商品已被抢购一空”等等。

这里分享一下星光图床的本地化实践代码:

//接口错误码
TID_API_CODE_200: '请求成功',
TID_API_CODE_504: '请求超时',
TID_API_CODE_999999: '服务器内部错误',
TID_API_CODE_100000: '请求参数有误',
TID_API_CODE_100001: '登录状态失效',
TID_API_CODE_100002: '人机验证不通过',
TID_API_CODE_100003: '验证码不正确',
TID_API_CODE_100004: '账号不存在',
TID_API_CODE_100005: '账号密码不匹配',
TID_API_CODE_100006: '找不到此订阅信息',
TID_API_CODE_100007: '发送频率超出限制',
TID_API_CODE_100008: '发送次数超出限制',
TID_API_CODE_100009: '空间不存在',
TID_API_CODE_100010: '空间已存在',
TID_API_CODE_100011: '空间不为空',
TID_API_CODE_100012: '操作太过频繁',
TID_API_CODE_100013: '空间数量配额耗尽',
TID_API_CODE_100014: '文件数量配额耗尽',
TID_API_CODE_100015: '传输流量配额耗尽',
TID_API_CODE_100016: '订阅计划不匹配',
TID_API_CODE_100017: '空间文件访问受限',
TID_API_CODE_100018: '上传文件超出大小限制',
TID_API_CODE_100019: '上传文件格式不支持',
...

前端或客户端在请求拦截器里面可以做统一处理:

//重写错误消息
let message = $t(`TID_API_CODE_${respData.code}`)

$t是博主自己实现的一个根据语言代码获取语言文字的函数,逻辑比较简单,这里就不赘述了。

读到这里,稍微有些许的认同感了吗?😛

另外,上面一系列的业务码可能都会影响到另一个字段值success,让success变为false。这一点后面详说。

message

提示消息,当你定义好业务码枚举并使用RegisterCodeTable函数注册好之后,go-sail会在你调用的时候,自动帮你维护提示消息。这个提示消息在我看来更多的是给开发人员或是本地化不完善的情况下做兜底的方案。例如上述的code=1000的时候,本地化没有找到对应的语言代码,可以降级使用接口返回的message字段值,显示效果可能从“您的余额不足”变成了"Insufficient balance",但好歹能用不是?

尽管如此,go-sail依然提供了多语言业务码的注册功能,你可以在服务端做上述的前端或客户端本地化的工作。至于采用哪种方案,那需要看你的具体业务需求。

requestId

唯一请求标识符,这个字段可以说为问题排查奠定了坚实的基础。go-sail提供了调用链日志追踪功能,也就是可观测性的一隅。

当你的接口响应出现错误时,可以依据requestId进行调用链追踪,方便问题排查:

GVMadgLbkAAcLmr.jpeg(via stardots.io)

GVMalvqbwAAjM1w.jpeg(via stardots.io)

图中博主使用的是ELK日志收集,当然你也可以从你的本地文件用命令来进行查看。go-sail的日志收集也同样支持本地文件输出。

tail -f -n 100 ~/your-logfile.log |grep 72a99ac0

日志输出到文件做了日志轮转功能,不会把你的磁盘塞满,把心放到肚子里。

这个时候可以说不再像以前一样遇到问题两眼一抹黑了。😎

你的产品经理甚至可以要求前端或客户端同学,将requestId放到提示弹窗里面,这样出现问题时,用户反馈截图即可解决问题!

像这样:
iShot_2024-12-18_22.01.31.png(via stardots.io)

提示:图中是对requestId截取了一部分显示出来。

对用户来说几乎无感。悄悄的,你就把事情给办了。🐮

success

表示业务逻辑是否成功。go-sail会根据你调用时设置的code码,自动维护success的值,前端或客户端可以依据此值断言业务逻辑处理是否成功。甚至可以根据这个值决定是渲染绿色的提示弹窗还是黄色或红色的提示弹窗。怎么样,有意思吗?

ts

即timestamp的缩写,表示系统当前毫秒时间戳。我在设计之初考虑到的两点用途:

  • 当前端或客户端需要严格比对服务器时间的时候,例如产品预售倒计时等等。特别是在你的产品是面向全球用户的时候特别实用。
  • 用户定位问题、请求、操作的时间。前端或客户端可以将这个值记录到本地或是直接展示出来,用于协助问题排查。

为什么是毫秒?

如果你是一个Web前端开发者,可能你会抿嘴一笑,是的,Date对象的构造函数接收的就是一个毫秒时间戳:

let date = new Date(ts)

拿来就直接用,不必再思考是不是还要乘以1000,降低了使用时的心智负担。除此之外,毫秒的粒度足够覆盖绝大多数的应用场景了。

data

业务数据实体。这个字段就是装载具体业务逻辑的响应数据,或是数组,或是map,或是结构体或是简单的数字、字符串布尔值,都可以。

需要说明的是,字段名的命名并不重要,重要的是结构明确,思路清晰。跟着博主一起聊完了本文的主要内容,现在感觉如何?欢迎在评论区一起分享。

最后,欢迎大家关注go-sail开源项目:

仓库地址: https://github.com/keepchen/go-sail


980_120.png(via stardots.io)

觉得博主写的不错?给他一个赞赏:

keepchen的赞赏码-支付宝 keepchen的赞赏码-微信