前言:

小编好几个月都没更新啦。注意力都在体验幸福甜蜜的的生活在。由于注意力被分散,在此期间,
耳机掉了两副,小米手环掉了两个,周围发生的事情间接性忽略掉了,恍然间回过神来已过去几个
月。蓦然回首时间过滴好快呀。得给宝子们来点干货

强类型语言

数据绑定

在强类型语言中,绑定参数分好几种类型。拿最使用广泛的Gin web 框架介绍

1、Bind 和 ShouldBind

Gin 的 Context 为请求数据绑定提供了两大类方法:在命名上以 Bind 为前缀和以 ShouldBind区分。这两大类方法在行为上有些差异。
类型描述
must条件必须要满足
should条件应该满足

Bind 方法会自动将 http status 设置为 400。而且不会返回更多的信息。由于存在,错误处理复杂,限制较多(仅仅支持常见类型),验证规则限制简单,可能存在安全风险等等在日常开发中几乎不建议使用bind 来绑定数据

ShouldBind、ShouldBindJSON 这些方法的区别是前者会自动根据 Header 头确定使用什么绑定器,如果团队内开发规范里约定了请求 Content-Type 都是 JSON 的话,直接选用后者更为合理。
在实际开发中,ShouldBind 自动根据Header 头确定使用什么绑定器使用最多。因为团队的约定对新加入的团队的人来说没看到约定的情况下,容易出现代码不兼容的问题。

var request struct {
  Email string `form:"email" binding:"required"`
  Name  string `form:"name" binding:"required"`
}
// ShouldBind 常规绑定也可以兼容post get json 参数
if err := ctx.ShouldBind(&request); err != nil {
  core.Response(ctx, gin.H{}, err)
  return
}
// Content-Type 都是 JSON 的话,对于强类型语言来说,使用下面这段更合理,一般情况还是用上面这种。
if err := ctx.ShouldBindJson(&request); err != nil {
  core.Response(ctx, gin.H{}, err)
  return
}

2、单次绑定和多次绑定request body 数据

有了上面的绑定基础情况下,小编遇到了这样的一个问题。权限验证中间件,在接收参数的时候使用的下面代码的接收参数的方式。接口的请求参数是Content-Type 都是 JSON 的类型的。

message := ctx.PostForm("message")
ids := ctx.QueryMap("ids")
message := ctx.GetQuery("message")

此时此刻,为了让接口绑定参数和中间件绑定参数,就会有二次绑定的数据的问题。小编楞是找了半天,原来强类型语言,几乎对类型都有做区分。不管是json get post request 类型每种类型都做了区分,连二次绑定都做了区分,不得不说,为了性能牺牲编写代码的体验。所以关键来了

无论是 Bind 还是 ShouldBind 类的绑定方法,都只能读取一次请求体进行绑定

可以使用ShouldBindBodyWith 来实现二次及多次绑定。那问题又来了,使用了ShouldBindBodyWith 绑定从一个参数被绑定开始,后面的绑定都得使用ShouldBindBodyWith 来绑定,有这样的限制才能实现多次绑定

if err := ctx.ShouldBindBodyWith(&request,binding.JSON); err != nil {
  core.Response(ctx, gin.H{}, err)
  return
}

到了这里基本上对参数绑定有了一定的了解。不同类型需要相互转换,不同的的场景需要不同的方法来实现。相对于弱类型语言PHP来说,不管三七二一,一键全部接收,不管什么类型,一次绑定还是多次绑定也好,照单全收,这样的处理也间接导致弱类型语言在底层处理上需要兼容各种类型,性能也相对于强类型语言编译需要更多的内容空间和消耗更多的CPU资源。

3、通用类型绑定

在强类型语言中,接收参数的时候,常用结构体来绑定数据类型。每种结构体定义一种类型。而且首字母要大写。

type request struct {
  Email string `form:"email" binding:"required"`
  Name  string `form:"name" binding:"required"`
  Age   int     `form:"age" binding:"required"`
}

那么问题来了,要接收数据格式为json 数据格式怎么办。貌似单一的数据string int 等数据格式都不能满足了,需要嵌套结构体如下:

type Grouping struct {
    ID          int         `json:"id"`
    Remark      string      `json:"remark" default:""`
}
type Group struct {
    Test1   []*Grouping `json:"test1"`
    Test2   Grouping    `json:"test2"`
    Test3   string      `json:"test3"`
    Test4   int         `json:"test4"`
}

那么问题又来了,这种类型是json 格式固定的情况下,那么需求有变动,就得更新代码新增结构体字段。增加一个结构体字段,就得重新编译一次。那没有接收通用类型的字段值。

接下来 interface 类型就派上用场了。interface 类型为通用类型,MySQL 数据库没有这种类型。还需要将interface 类型转成 json,string,int 这样的类型才能保存。所以在接收通用数据参数格式的时候,得借助MySQL第三方扩展库在标识,接收通用类型是 text string 还是json 类型

type UserGroupTask struct {
    Test1  interface{} `json:"test1" form:"test1" gorm:"type:'json'"`
}

通过标识 gorm 标识,interface 类型为json。这样在入MySQL数据库的时候,才能识别出是JSON 格式。

总结

上面介绍了强类型语言,需要对字段类型做转化、数据绑定类型指定等,在弱类型语言中在底层封装好了。
留下一个疑问,上面的代码接收通用格式,怎么在尽可能少写结果体的前提下,把json 格式的参数取出来做判断后,在入到数据库里去呢。


叶剑飞雪
137 声望9 粉丝