likes
comments
collection
share

「容器管理系统」开发篇:4. Gin 如何优雅的使用 struct 的 tag 标签

作者站长头像
站长
· 阅读数 64

回顾

项目已开源:基于 Golang 的 容器管理系统

Gin 请求参数绑定解读

在使用 Gin Web 框架时,接收参数一般是这样操作:

  1. 定义接收参数的 struct 结构体
type ParamsRequest struct{
    UserName string `json:"user_name" form:"user_name"`
    ...
}
  1. 使用 ShouldBind 系列函数,对接收的 struct 结构体进行赋值,会识别 struct 中的 tag 标签

ShouldBind 系列函数包含:

ShouldBind、ShouldBindJSON、ShouldBindXML、ShouldBindQuery、ShouldBindYAML、ShouldBindTOML、ShouldBindHeader、ShouldBindUri、ShouldBindWith、ShouldBindBodyWith

10 个函数,用来获取各种格式的请求。使用方式可以参考 Gin 源码 context.go 这个文件。

这里咱们就主要讲解前两个函数: ShouldBindShouldBindJSON

注意: ShouldBind 系列函数 会默认识别 form tag 标签,如果没有设置 form tag 标签,会退而求其次,识别 json tag 标签,如果根本就没有设置 tag, 会使用 struct 的属性名

所以:在请求传参时,需要根据 structtag 标签的名字进行传参才会识别到数据,进行赋值

  • ShouldBind 是一个通用获取,可以根据请求类型和请求的内容类型 Content-Type 来自动识别是什么格式的请求,走对应的格式进行赋值,一般不指定请求格式的就使用这个
var params ParamsRequest
if err := c.ShouldBind(&params); err != nil {
    return
}
  • ShouldBindJSON 是用来专门获取 json 请求的,前置条件是: struct 中 设置对应的 json tag 标签
var params ParamsRequest
if err := c.ShouldBindJSON(&params); err != nil {
    return
}

项目中咱们使用的:ShouldBindJSON, 通过上面的解读,是不是认为 Gin 的参数获取已经做得很好了,可以直接进行赋值拿到了,下面直接用就好了,话是这么说,在这里我提出一个问题: 拿到参数之后,对数据库操作,你会在怎么做呢?

如何操作 structtag

这里有以下方法可以操作:

  • 通过官方 reflect 反射包解析 tag 标签
func ToTags(c any, tagName string) (fields []string) {
    tagFields := []reflect.StructField{}

    ct := reflect.TypeOf(c)
    for i := 0; i < ct.NumField(); i++ {
       tagFields = append(tagFields, ct.Field(i))
    }

    for _, val := range tagFields {
       tagValue := val.Tag.Get(tagName)
       if tagValue != "" {
          fields = append(fields, strings.Split(tagValue, ",")[0])
       }
    }
    return fields
}

structs.ToTags(any, "json")

上面这段代码可以解析到 tag 标签,并返回出一个 字符数组进行接收,但是你解析出来之后,如何对你定义的数据库 modelstruct 进行赋值呢?

如何将传参的 struct 优雅的赋值到 modelstruct 中?

这里使用到了 encoding/json 官方 JSON

  • 定义一个 model structparams struct

type ParamsRequest struct {
    IdOne uint32 `json:"id_one"`
    IdTwo string `json:"id_two"`
}

type ModelStruct struct {
    IdOne uint32 `json:"id_one"`
    IdTwo uint32 `json:"id_two"`
}
  • 实现函数:
func (c *ModelStruct) ParseFields(p any) *ModelStruct {
    if p == nil {
       return c
    }
    pjson, err := json.Marshal(p)
    if err != nil {
       return c
    }

    err = json.Unmarshal(pjson, c)
    if err != nil {
       return c
    }
    return c
}

解析步骤:

  • 第一步: 使用 json.Marshalparams struct 转换为 params []byte
  • 第二步: 使用 json.Unmarshalparams []byte 转换为 model struct
  • 第三步: 重新返回 model struct (这一步是返回新的 model struct 避免重复赋值)

结束语

  • 使用了 encoding/json 包的特性:json.Marshal、json.Unmarshal
  • json.Marshal 可以将指定的 struct 序列化成 json 格式的数据
  • json.Unmarshal 可以将任意 json 格式的数据转换到指定的 struct

这里的 struct 不规定是同一个,可以使用不同的 struct 进行相互转换,只要对应的 struct 有对应的属性和 tag 标签,就可以识别到。

转载自:https://juejin.cn/post/7291935623477362715
评论
请登录