首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go API 多种响应的规范化处理和简化策略

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

作者头像
KevinYan
发布2024-11-23 14:32:44
发布2024-11-23 14:32:44
32500
代码可运行
举报
文章被收录于专栏:网管叨bi叨网管叨bi叨
运行总次数:0
代码可运行

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

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

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

‍‍

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

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

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

  • 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

代码语言:javascript
代码运行次数:0
运行
复制
.
|-- common
|   |-- app
|       |---pagination.go
|       |---response.go
|......
|-- main.go
|-- go.mod
|-- go.sum

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

代码语言:javascript
代码运行次数:0
运行
复制
type response struct {
 ctx        *gin.Context
 Code       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文件中。

代码语言:javascript
代码运行次数:0
运行
复制
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 中提供的方法。

代码语言:javascript
代码运行次数:0
运行
复制
// SetPagination 设置Response的分页信息
func (r *response) SetPagination(pagination *Pagination) *response {
 r.Pagination = pagination
 return 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 = requestId
 r.Data = data

 r.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

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

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

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

代码语言:javascript
代码运行次数:0
运行
复制
 g.GET("/response-obj", func(c *gin.Context) {

  data := map[string]int{
   "a": 1,
   "b": 2,
  }
  app.NewResponse(c).Success(data)
  return
 })

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

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

代码语言:javascript
代码运行次数:0
运行
复制
 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了一个错误进行了返回,运行项目访问接口会看到下面的结果

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

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网管叨bi叨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 确定项目接口响应的通用格式
  • 封装响应组件
  • 用组件返回成功和错误响应
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档