参数验证

* 本页面主要介绍Go语言参数验证这一主题的相关内容。

在我们的实际业务场景中,经常会有这样的情况出现:要对接口struct字段进行参数验证。比如:

  • Uid的值在某一个范围内
  • Name值的长度在某一个范围内
  • Sex的值符合男或女
  • Email格式正确等等

其实在godoc里可以搜到很多第三方数据校验的模块,今天给大家推荐的是 asaskevich/govalidator,原因如下:

  • star最多、持续更新发布
  • 功能完善、使用便利
  • 丰富的字符串校验、数据匹配、裁剪拼接处理等
  • 支持struct元素合法性校验,并且支持嵌套检查
  • 源码也值得一读,值得学习,就是一个百宝箱

这个项目的地址是: https://github.com/asaskevich/govalidator,详细的文档地址是:https://godoc.org/github.com/asaskevich/govalidator

通过下面的方式就可以“安装” asaskevich/govalidator ,然后在需要使用的地方导入即可。如果你使用了Go module来管理第三方包,那么你可以参考 使用了Go mod管理的项目里如何添加新的第三方包? 来详细了解如何导入一个新的第三方包到本地项目中。

govalidator安装
govalidator安装

通过上面的一顿操作,恭喜你已经可以尽情体验 govalidator 中的所有功能了!

govalidator导入vendor及使用
govalidator导入vendor及使用

今天,我们就围绕 asaskevich/govalidator来介绍如何完成“各种各样”的参数验证。

一、最简单的字符串匹配

govalidator的使用方法非常简单,只需要import导入这个包即可,然后就可以调用相应的方法进行参数校验了。

  package main

  import (
      "fmt"
      "github.com/asaskevich/govalidator"
  )

  func main() {
      // 判断字符串值是否为合法的IPv4地址
      ip4 := "192.168.1.1"
      fmt.Println(govalidator.IsIPv4(ip4)) // true

      // 判断字符串值是否为合法的MAC
      mac := "aa:bb:cc:dd:ee:ffffff"
      fmt.Println(govalidator.IsMAC(mac)) // false

      // 判断数字是否在指定范围内
      dig := 101    // string类型也可以用
      fmt.Println(govalidator.InRange(dig, 0, 100)) // false
  }

  // 输出结果
  true
  false
  false
            
二、struct元素匹配

govalidator专门提供了一个函数,用于校验struct的元素: govalidator.ValidateStruct()

  package main

  import (
      "fmt"
      "github.com/asaskevich/govalidator"
  )

  type foo struct {
      A string `valid:"ipv4"`
      B string `valid:"mac"`
      C string `valid:"range(0|100)"`    // 也可以使用int类型
  }

  func main() {
      f := foo{
          A: "192.168.1.1",
          B: "aa:bb:cc:dd:ee:ffffff",
          C: "101",
      }

      result, err := govalidator.ValidateStruct(f)
      if err != nil {
          fmt.Println("error: " + err.Error())
      }
      fmt.Println(result)
  }

  // 输出
  error: B: aa:bb:cc:dd:ee:ffffff does not validate as mac;C: 101 does not validate as range(0|100)
  false
            

这里有一些需要注意的点需要强调一下:

  • struct元素只支持部分常用的校验,详细可以参考官方文档:https://github.com/asaskevich/govalidator#validatestruct-2
  • struct元素必须是导出型,也就是必须大写字母开头,govalidator才会去做校验
  • struct元素匹配较为智能,比如range(min|max)不仅支持string也支持int类型
三、struct元素可选验证

govalidator有一个bool类型的全局变量,可通过函数 govalidator.SetFieldsRequiredByDefault()进行设置,通常情况下,在init函数或者main中进行初始化。

  • true时,如果没有定义valid tag,则会提示错误
  •   //假设一个struct元素的值为空字符""(即zero value)情况下的表现
      `valid:""`    
      // 报错:All fields are required to at least have one validation defined
      `valid:"-"`    // true
      `valid:","`    // 报错:Missing required field
      `valid:",optional`    // true
      `valid:",required`    // 报错:non zero value required
      `valid:"ipv4"`    // 报错:Missing required field
      `valid:"ipv4,optional"`    // true
      `valid:"ipv4,required"`    // 报错:non zero value required
  • false时(默认值),如果没有定义valid tag,不会提示错误
  •   //假设一个struct元素的值为空字符""(即zero value)情况下的表现
      `valid:""`    // true
      `valid:"-"`    // true
      `valid:","`    // true
      `valid:",optional`    // true
      `valid:",required`    // non zero value required
      `valid:"ipv4"`    // true
      `valid:"ipv4,optional"`    // true
      `valid:"ipv4,required"`    // 报错:non zero value required

另外在valid tag里,可以通过显式设置方式更细颗粒度地控制:当遇到“zero value”时是需要验证还是提示错误。此设置会覆盖调上面这个全局变量的设置

  `valid:""` // 等同于空tag,即``
  `valid:"-"` //可免除,即可选的,不是必须字段
  `valid:","`
  `valid:",optional` //optional可选的,不是必须字段
  `valid:",required`

还有一个全局变量nilPtrAllowedByRequired,通过govalidator.SetNilPtrAllowedByRequired()设置。这个全局变量默认是false,当由required标记的struct字段设置为nil时,SetNilPtrAllowedByRequired会导致验证通过。

  package main

  import (
    "fmt"
    "github.com/asaskevich/govalidator"
    )

  type myStruct struct {
    A []string `valid:",required"`
    B int
  }

  func main() {
    var a myStruct
    x,err := govalidator.ValidateStruct(a)
    if err != nil {
      fmt.Println("error: " + err.Error())
    }
    fmt.Println(x)
  }

上面的情况下会输出错误:error: A: non zero value required

四、struct元素嵌套校验

嵌套元素名必须是导出型,也就是大写字母开头。

  type Foo struct {
      A string `valid:"ipv4"`
      B string `valid:"mac"`
      C int `valid:"range(0|100)"`
  }

  type bar struct {
      X string `valid:"ipv4"`
      Foo `valid:",required"`
  }

  func main() {
      govalidator.SetFieldsRequiredByDefault(true)

      b := bar{
          X: "192.168.1.1",
      }

      b.Foo.A = "192.168.1.1.1"
      b.Foo.B = "aa:bb:cc:dd:ee:ff"
      b.Foo.C = 100

      result, err := govalidator.ValidateStruct(b)
      if err != nil {
          fmt.Println("error: " + err.Error())
      }
      fmt.Println(result)
  }
  //输出
  error: Foo.A: 192.168.1.1.1 does not validate as ipv4;A: 192.168.1.1.1 does not validate as ipv4
  false

关于参数校验的内容今天就讨论到这里,更多深入的研究大家可以参考官方文档或者阅读下源码,会对大家非常有帮助。


* 本页内容参考以下数据源:

  • https://www.cnblogs.com/fzxiaomange/p/golang-govalidator.html
  • https://www.jianshu.com/p/64757be312a4
  • https://github.com/asaskevich/govalidator
  • https://ask.zkbhj.com/?/question/394

凯冰科技 · 代码改变世界,技术改变生活
Next Page→