Go API 多种响应的规范化处理和简化策略

news/2024/11/7 10:18:04/

一个对外提供API接口的服务,在真正动工开发接口前一般需要先确定一下接口响应的通用格式,无论接口响应里返不返回业务数据,返回的数据是字符串、列表、对象还是其他类型都会遵照这个通用的响应格式。

既然一个项目接口的响应格式是确定的,那么在搭建项目的时候就需要我们提前封装一个通用的接口响应组件,让实现业务逻辑的代码能尽量傻瓜式地调用响应组件,由响应组件负责生成响应返回给客户端。

这篇内容我跟大家一起分析项目接口响应的通用格式应该是什么样的,然后动手为Go项目封装一个统一的接口响应组件让它能为项目生成通用格式的响应,该组件还会对返回分页数据的接口做一个逻辑简化,为错误响应做好兜底。大家跟着我一起来看看吧。

b974bbee249e3c07904f4091c41253f4.jpeg

‍ ‍

本节对应的代码版本为c5,订阅后加入课程的GitHub项目后可以直接查看本章节对应的代码更新

6d6bb1603a42c53885288a14c58a15cf.png

请扫码订阅专栏,即可加入实战项目获得配套的完整实战教程

e813b0d6c962538ca501370c2161bc0f.png

确定项目接口响应的通用格式

一般的响应格式必须有这么几个要素:

  • code : 响应中的业务Code码,一般0表示成功,其他码值会对应到不同的错误上,在Go项目Error的统一规划管理策略中已经教大家怎么按模块管理Error了,响应组件会直接使用那些预定义Error上的code码值作为响应code。

  • msg: 这个好理解就是个信息字符串,有可能前端会以这个值作为客户端的toast 消息。

  • data: 接口中返回的数据,可能是对象也可能是列表,这个就需要负责各个接口的前端组件去对应解析啦

  • request_id: 有的团队会要求返回这个request_id ,不是必须的,但是有它,需要查数据的时候会更好的从日志里回溯请求在服务端都发生了什么。

  • pagination: 接口返回列表数据,有可能需要返回总行数之类的信息,好去请求下一页数据,一般在管理后台类的项目中使用较多, 移动端可能会更喜欢拿数据的last id 去请求下一批数据。

确定好接口响应的通用格式后,接下来我们开始为项目封装响应组件。

封装响应组件

我们先在 common 目录下新建 app 目录,其中新增两个文件 response.go 和 pagination.go

.
|-- common
|   |-- app
|       |---pagination.go
|       |---response.go
|......
|-- main.go
|-- go.mod
|-- go.sum

在 response.go 定义项目接口的统一响应结构

type response struct {ctx        *gin.ContextCode       int         `json:"code"`Msg        string      `json:"msg"`RequestId  string      `json:"request_id"`Data       interface{} `json:"data,omitempty"`Pagination *Pagination `json:"pagination,omitempty"`
}

response 中的 Pagination 是分页信息,其结构定义在pagination.go文件中。

type Pagination struct {Page      int `json:"page"`PageSize  int `json:"page_size"`TotalRows int `json:"total_rows"`
}

reponse定义中 Data 和 Pagination 的结构体 tag 中 都有一个 json:"xxx,omitempty"这个 omitempty 的意思是进行JSON格式化的时候忽略空值。

比如我们的API返回单一的对象或者不需要分页的列表信息时不会设置响应的分页信息,加上这个标签后接口的响应结果中就不会有pagination这个字段了。data字段也是同一个道理。

所以我们分别给response定义了 SuccessOk和Success方法,前一个情况接口程序直接调用SuccessOk即返回不带数据的成功响应,后者返回带数据的接口响应

我们来看一下 response 中提供的方法。

// SetPagination 设置Response的分页信息
func (r *response) SetPagination(pagination *Pagination) *response {r.Pagination = paginationreturn r
}func (r *response) Success(data interface{}) {r.Code = errcode.Success.Code()r.Msg = errcode.Success.Msg()requestId := ""if _, exists := r.ctx.Get("traceid"); exists {val, _ := r.ctx.Get("traceid")requestId = val.(string)}r.RequestId = requestIdr.Data = datar.ctx.JSON(errcode.Success.HttpStatusCode(), r)
}func (r *response) SuccessOk() {r.Success("")
}func (r *response) Error(err *errcode.AppError) {r.Code = err.Code()r.Msg = err.Msg()requestId := ""if _, exists := r.ctx.Get("traceid"); exists {val, _ := r.ctx.Get("traceid")requestId = val.(string)}r.RequestId = requestId// 兜底记一条响应错误, 项目自定义的AppError中有错误链条, 方便出错后排查问题logger.New(r.ctx).Error("api_response_error", "err", err)r.ctx.JSON(err.HttpStatusCode(), r)
}
  • SetPagination 用来设置响应的分页信息

  • Success 返回接口执行符合预期的成功响应,其中会携带Data数据返回给客户端。

  • SuccessOk 针对只需要知道成功状态的接口响应,目的是简化接口程序的调用。在这种情况下不需要使用一个空字符串或者nil参数去调用Success方法。

  • Error 返回错误响应,参数为我们为项目定义的AppError对象,这样响应码使用的既是AppError的Code码,在返回错误响应时会记录一条错误响应,这样即使你在处理程序中没有打错误日志,框架这里也能做个兜底,方便出错后排查问题。

接口响应里的requestId 我们取的是当次请求对应的tracceid这样requestId 也能跟我们本次请求的所有日志中携带的traceid 对应起来,具体可参前面的文章Go日志门面的设计与实现-自动注入追踪ID

用组件返回成功和错误响应

接下来我们在项目中写几个简单的接口测试一下组件的功能。

先写一个返回返回对象信息的测试接口。

g.GET("/response-obj", func(c *gin.Context) {data := map[string]int{"a": 1,"b": 2,}app.NewResponse(c).Success(data)return})

运行项目后访问接口会看到以下结果。

93a0a17f213295f7b6b2d373ab986f1a.png

再来一个返回错误响应的测试接口。

g.GET("/response-error", func(c *gin.Context) {baseErr := errors.New("a dao error")// 这一步正式开发时写在service层err := errcode.Wrap("encountered an error when xxx service did xxx", baseErr)app.NewResponse(c).Error(errcode.ErrServer.WithCause(err))return})

这里是Mock了一个错误进行了返回,运行项目访问接口会看到下面的结果

fbd2a8904f5b9f203e4ed7d6c01a7384.png

返回错误响应时,我并没有记错误日志,但是的组件会帮我们兜底记了一条响应错误的日志, 防止开发中忘了在程序中打错误日志。

b0bd4e6dfbb766a0cbf14c656e6fe5fc.png

结合我们在《学会定制化 Go 项目的 error,回溯错误的原因和发生位置》给项目Error增加了错误原因链和发生位置记录的功能,这样一来,即使你在开发过程中全程都没有打日志,也不至于出问题后查不到相关的信息。

接下来组件在返回分页数据时怎么简化项目中分页的代码逻辑,请订阅《Go项目搭建和整洁开发实战》专栏阅读剩余内容。

本专栏力主实战技能,配备完整的实战项目,扫下方二维码即可订阅。

cfea40e1a6a644043c2f7c2b6bbb37cf.png

 订阅后,可加入专栏配套的实战项目,获得完整实战教程,同时也有专属的读者群,欢迎加入一起学习

专栏分为五大部分 (前三部分25节内容已经更新完成) 主要内容架构如下:

38d270b220510547d356dc77f9a94c99.png

  • 第一部分介绍让框架变得好用的诸多实战技巧,比如通过自定义日志门面让项目日志更简单易用、支持自动记录请求的追踪信息和程序位置信息、通过自定义Error在实现Go error接口的同时支持给给错误添加错误链,方便追溯错误源头。

  • 第二部分:讲解项目分层架构的设计和划分业务模块的方法和标准,让你以后无论遇到什么项目都能按这套标准自己划分出模块和逻辑分层。后面几个部分均是该部分所讲内容的实践。

  • 第三部分:设计实现一个套支持多平台登录,Token泄露检测、同平台多设备登录互踢功能的用户认证体系,这套用户认证体系既可以在你未来开发产品时直接应用

  • 第四部分:商城app C端接口功能的实现,强化分层架构实现的讲解,这里还会讲解用责任链、策略和模版等设计模式去解决订单结算促销、支付方式支付场景等多种多样的实际问题。

  • 第五部分:单元测试、项目Docker镜像、K8s部署和服务保障相关的一些基础内容和注意事项

扫描上方的海报二维码或者访问 https://xiaobot.net/p/golang 即刻订阅

点击阅读原文可跳转至本文的完整版。


http://www.ppmy.cn/news/1545030.html

相关文章

在 MacOS 上跑 kaldi

categories: [asr] tags: C asr kaldi 在MacOS 下跑 kaldi brew install automake llvm cmake sox libtool subversion基本安装 Common build problems pyenv/pyenv Wiki; brew install pyenv pyenv install -v 2.7.18# Set the python version.pyenv global 2.7.18 # Expor…

【Vue 全家桶】7、Vue UI组件库(更新中)

目录 Element UI移动三级目录 Element UI 移动 三级目录

udp丢包问题

udp或者tcp丢包问题监测方式: netstat -su 问题分析: 1. 内存 2. cpu 3. 发送接收缓存 动画图解 socket 缓冲区的那些事儿-CSDN博客

51c自动驾驶~合集5

我自己的原文哦~ https://blog.51cto.com/whaosoft/11563178 #MapDistill 速度精度双起飞,让End2End更丝滑 在线高精(HD)地图构建是自动驾驶领域的一项重要且具有挑战性的任务。最近,人们对不依赖于激光雷达等其他传感器的基于…

Vue2中使用firefox的pdfjs进行文件文件流预览

文章目录 1.使用场景2. 使用方式1. npm 包下载,[点击查看](https://www.npmjs.com/package/pdfjs-dist)2. 官网下载1. 放到public文件夹下面2. 官网下载地址[点我,进入官网](https://github.com/mozilla/pdf.js/tags?afterv3.3.122) 3. 代码演示4. 图片预览5. 如果遇到跨域或者…

无人机避障——大疆与Airsim中的角速度信息订阅获取

本文先将Airsim仿真中的角速度信息获取弄好,然后再将大疆SDK中的角速度话题订阅一下,并验证获取角速度信息,后续为DWA动态窗口法替代PID作为局部路径规划做足准备。 Airsim中的角速度信息获取 Airsim无人机状态获取:getMultirot…

数据结构题集-第二章-线性表-连接链表并安全释放

说明 本文参照严蔚敏《数据结构(C语言版)题集》一书中包含的问答题和算法设计题目,提供解答和算法的解决方案。请读者在自己已经解决了某个题目或进行了充分的思考之后,再参考本解答,以保证复习效果。由于作者水平所限,本解答中一…

基于Spring Boot和Vue的电子商城系统功能设计

基于Spring Boot和Vue的电子商城系统功能设计 该系统是一个基于Spring Boot和Vue框架的电子商城平台,包含前台商城和后台管理系统。系统功能设计包括用户购物体验和管理员管理功能,支持商品的分类展示、收藏、购物车和订单管理等模块。以下是系统功能的简…