• 你好!欢迎你的到来
  • 关于我们
  • 首页 博客 学习笔记 技术导航 工具
  • 博文分类
    • PHP(43)
    • MySQL(11)
    • Linux(28)
    • html(3)
    • JQuery(4)
    • JavaScript(9)
    • svn(2)
    • CSS(2)
    • seajs(1)
    • go(44)
    • redis(1)
    • nginx(8)
    • mongo(0)
    • java(0)
    • 算法(0)
    • 其他(26)
    • 生活(1)
    专栏
    • Jquery基础教程
      • 文章:(15)篇
      • 阅读:28436
    • shell命令
      • 文章:(42)篇
      • 阅读:94037
    • Git教程
      • 文章:(36)篇
      • 阅读:162119
    • leetCode刷题
      • 文章:(76)篇
      • 阅读:43231
    • 摘要视图
    • 目录视图
    go-playground/validator的使用
    2020-03-22 21:24 阅读(11706) 评论(0)

    一、简介

    在上一篇文章“关于go-playground/validator源码分析”,大致过了一遍validator源码,了解了基于tag中规则验证数据的原理,本文我们将从使用角度说说validator,如何使用tag规则来验证数据。首先,我们来看看validator提供哪些规则,如下,针对常用的,做了一些备注说明

    "required":             hasValue, // 是否必填,即不能为零值
    "required_with":        requiredWith, // 关联矫正,如required_with=Name 若Name字段不为零值,则当前字段不能为零值
    "required_with_all":    requiredWithAll,// 多个字段都不为空的时候,才会矫正当前字段
    "required_without":     requiredWithout, // 当某个字段为零值的时候,才会矫正当前字段
    "required_without_all": requiredWithoutAll,// 当多个字段都为零值的时候,才会矫正当前字段
    "isdefault":            isDefault, // 当前字段是否为零值
    "len":                  hasLengthOf, // 针对数组、切片、map、string,判断长度是否为len对应的值,针对int、float、uint等,判断当前字段值是否为len对应的值
    "min":                  hasMinOf, //用于验证当前字段的值是否大于或等于参数的值,若当前字段为数组、切片、map,则将它们的长度用于比较
    "max":                  hasMaxOf,//用于验证当前字段的值是否大于或等于参数的值,若当前字段为数组、切片、map,则将它们的长度用于比较
    "eq":                   isEq,// 判断是否相当,若当前字段为数组、切片、map,则将它们的长度用于比较
    "ne":                   isNe,// 判断是否不等于
    "lt":                   isLt, // 判断是否小于
    "lte":                  isLte,// 判断是否小于等于
    "gt":                   isGt,// 判断是否大于
    "gte":                  isGte,// 判断是否大于等于
    "eqfield":              isEqField, // 和另外一个字段进行比较,判断是否相当,可用于比较注册时,密码和确认密码是否相等
    "eqcsfield":            isEqCrossStructField,
    "necsfield":            isNeCrossStructField,
    "gtcsfield":            isGtCrossStructField,
    "gtecsfield":           isGteCrossStructField,
    "ltcsfield":            isLtCrossStructField,
    "ltecsfield":           isLteCrossStructField,
    "nefield":              isNeField,
    "gtefield":             isGteField,
    "gtfield":              isGtField,
    "ltefield":             isLteField,
    "ltfield":              isLtField,
    "fieldcontains":        fieldContains,
    "fieldexcludes":        fieldExcludes,
    "alpha":                isAlpha,
    "alphanum":             isAlphanum,
    "alphaunicode":         isAlphaUnicode,
    "alphanumunicode":      isAlphanumUnicode,
    "numeric":              isNumeric,
    "number":               isNumber,
    "hexadecimal":          isHexadecimal,
    "hexcolor":             isHEXColor,
    "rgb":                  isRGB,
    "rgba":                 isRGBA,
    "hsl":                  isHSL,
    "hsla":                 isHSLA,
    "e164":                 isE164,
    "email":                isEmail,
    "url":                  isURL,
    "uri":                  isURI,
    "urn_rfc2141":          isUrnRFC2141, // RFC 2141
    "file":                 isFile,
    "base64":               isBase64,
    "base64url":            isBase64URL,
    "contains":             contains,
    "containsany":          containsAny,
    "containsrune":         containsRune,
    "excludes":             excludes,
    "excludesall":          excludesAll,
    "excludesrune":         excludesRune,
    "startswith":           startsWith,
    "endswith":             endsWith,
    "startsnotwith":        startsNotWith,
    "endsnotwith":          endsNotWith,
    "isbn":                 isISBN,
    "isbn10":               isISBN10,
    "isbn13":               isISBN13,
    "eth_addr":             isEthereumAddress,
    "btc_addr":             isBitcoinAddress,
    "btc_addr_bech32":      isBitcoinBech32Address,
    "uuid":                 isUUID,
    "uuid3":                isUUID3,
    "uuid4":                isUUID4,
    "uuid5":                isUUID5,
    "uuid_rfc4122":         isUUIDRFC4122,
    "uuid3_rfc4122":        isUUID3RFC4122,
    "uuid4_rfc4122":        isUUID4RFC4122,
    "uuid5_rfc4122":        isUUID5RFC4122,
    "ascii":                isASCII,
    "printascii":           isPrintableASCII,
    "multibyte":            hasMultiByteCharacter,
    "datauri":              isDataURI,
    "latitude":             isLatitude,
    "longitude":            isLongitude,
    "ssn":                  isSSN,
    "ipv4":                 isIPv4,
    "ipv6":                 isIPv6,
    "ip":                   isIP,
    "cidrv4":               isCIDRv4,
    "cidrv6":               isCIDRv6,
    "cidr":                 isCIDR,
    "tcp4_addr":            isTCP4AddrResolvable,
    "tcp6_addr":            isTCP6AddrResolvable,
    "tcp_addr":             isTCPAddrResolvable,
    "udp4_addr":            isUDP4AddrResolvable,
    "udp6_addr":            isUDP6AddrResolvable,
    "udp_addr":             isUDPAddrResolvable,
    "ip4_addr":             isIP4AddrResolvable,
    "ip6_addr":             isIP6AddrResolvable,
    "ip_addr":              isIPAddrResolvable,
    "unix_addr":            isUnixAddrResolvable,
    "mac":                  isMAC,
    "hostname":             isHostnameRFC952,  // RFC 952
    "hostname_rfc1123":     isHostnameRFC1123, // RFC 1123
    "fqdn":                 isFQDN,
    "unique":               isUnique, 用来矫正切片、数组元素是否全局唯一
    "oneof":                isOneOf,// 如`validate:"oneof=2 3 4"`,限制当前字段值只能为2或3或4
    "html":                 isHTML,
    "html_encoded":         isHTMLEncoded,
    "url_encoded":          isURLEncoded,
    "dir":                  isDir,
    "json":                 isJSON, // 判断当前值是否为有效的json字符串
    "hostname_port":        isHostnamePort,
    "lowercase":            isLowercase,
    "uppercase":            isUppercase,
    "datetime":             isDatetime, // 时间矫正,如 `validate:"datetime=2006-01-02"`

    二、简单的结构体验证

    type Student struct {
       Name string `validate:"required,min=2,max=10"` // 必填,最大长度 10 最小长度10
       Age  int64  `validate:"min=6,max=15"` // 最大值为15,最小值为6
       Num  string `validate:"required,len=6"` // 必填,长度必须为6
    }
    
    s := Student{
       Age:3,
       Num:"no9527",
       Name:"zhangsan",
    }
    
    validate := validator.New() // 创建验证器
    err := validate.Struct(s) // 执行验证
    
    if err != nil {
       fmt.Println(err) // 执行结果 Key: 'Student.Age' Error:Field validation for 'Age' failed on the 'min' tag
    }

    三、自定义tag验证

    有的时候,我们需要做一些自定义的业务验证,此时,我们就需要扩展tag验证函数。如下:

    type Student struct {
       Name string `validate:"required,min=2,max=10,unique_name"` // 必填,最大长度 10 最小长度10 unique_name为自定义tag,验证name唯一性
       Age  int64  `validate:"min=6,max=15"`                      // 最大值为15,最小值为6
       Num  string `validate:"required,len=6"`
    }
    
    s := Student{
       Age:  9,
       Num:  "no9527",
       Name: "zhangsan",
    }
    
    validate := validator.New()
    _ = validate.RegisterValidation("unique_name", CheckUniqueName) // 注册tag验证器
    
    err := validate.Struct(s)
    
    if err != nil {
       fmt.Println(err) // 执行结果 Key: 'Student.Name' Error:Field validation for 'Name' failed on the 'unique_name' tag
    }
    
    
    // 矫正名字的唯一性
    // fl 包含了字段相关所有信息
    func CheckUniqueName(fl validator.FieldLevel) bool {
       // 获取字段当前值 fl.Field()
       // 获取tag 对应的参数 fl.Param() ,针对unique_name标签 ,不需要参数
       // 获取字段名称 fl.FieldName()
    
       // balabala处理一波,比如查库比较
    
       return false
    }

    四、国际化支持

    在上面的两个案例中,验证失败的时候,报错为“Key: 'Student.Age' Error:Field validation for 'Age' failed on the 'min' tag”和“Key: 'Student.Name' Error:Field validation for 'Name' failed on the 'unique_name' tag”,这种报错是很不友好的,我们需要中文。如下:

    package main
    
    import (
       "fmt"
       "github.com/go-playground/locales/en"
       "github.com/go-playground/locales/zh"
       "github.com/go-playground/universal-translator"
       "github.com/go-playground/validator/v10"
       en_trans "github.com/go-playground/validator/v10/translations/en"
       zh_trans "github.com/go-playground/validator/v10/translations/zh"
    )
    
    type Student struct {
       Name string `validate:"required,min=2,max=10,unique_name"` // 必填,最大长度 10 最小长度10
       Age  int64  `validate:"min=6,max=15"`                      // 最大值为15,最小值为6
       Num  string `validate:"required,len=6"`
    }
    
    func main() {
       s := Student{
          Age:  2,
          Num:  "no9527",
          Name: "zhangsan",
       }
    
       // 创建翻译器
       zhTrans := zh.New() // 中文转换器
       enTrans := en.New() // 因为转换器
    
       uni := ut.New(zhTrans, zhTrans, enTrans) // 创建一个通用转换器
    
       curLocales := "zh"                        // 设置当前语言类型
       trans, _ := uni.GetTranslator(curLocales) // 获取对应语言的转换器
    
       validate := validator.New()                                     // 创建验证器
       _ = validate.RegisterValidation("unique_name", CheckUniqueName) // 注册自定义tag回调函数
    
       switch curLocales {
       case "zh":
          // 内置tag注册 中文翻译器
          _ = zh_trans.RegisterDefaultTranslations(validate, trans)
       case "en":
         // 内置tag注册 英文翻译器
          _ = en_trans.RegisterDefaultTranslations(validate, trans)
       }
    
       err := validate.Struct(s)
    
       if err != nil {
          errs := err.(validator.ValidationErrors)
          for _, e := range errs {
             // can translate each error one at a time.
             fmt.Println(e.Translate(trans))
          }
       }
    }
    
    // 矫正名字的唯一性
    // fl 包含了字段相关所有信息
    func CheckUniqueName(fl validator.FieldLevel) bool {
       // 获取字段当前值 fl.Field()
       // 获取tag 对应的参数 fl.Param() ,针对unique_name标签 ,不需要参数
       // 获取字段名称 fl.FieldName()
    
       // balabala处理一波,比如查库比较
    
       return false
    }

    执行结果如下:

    可以看出针对validator默认的tag验证规则,已翻译为中文了,但是针对自定义tag验证,仍然是英文的。怎么解决呢?

    这个时候,就需要注册validator翻译器了,如下:

    switch curLocales {
    case "zh":
       // 内置tag注册 中文翻译器
       _ = zh_trans.RegisterDefaultTranslations(validate, trans)
    
       // 自定义tag注册 中文翻译器
       _ = validate.RegisterTranslation("unique_name", trans, func(ut ut.Translator) error {
          if err := ut.Add("unique_name", "{0}已被占用", false); err != nil {
             return err
          }
    
          return nil
       }, func(ut ut.Translator, fe validator.FieldError) string {
          t, err := ut.T(fe.Tag(), fe.Field())
          if err != nil {
             log.Printf("警告: 翻译字段错误: %#v", fe)
             return fe.(error).Error()
          }
    
          return t
       })
    case "en":
       // 内置tag注册 英文翻译器
       _ = en_trans.RegisterDefaultTranslations(validate, trans)
    }

    执行结果如下:

    看着还不错,但是“Name已被占用”和“Age最小只能为6”中,“Name”和“Age”还没翻译呢?

    这个时候,我们可以注册RegisterTagNameFunc,如下:

    // 在tag中设置 label,代表中文翻译
    type Student struct {
       Name string `validate:"required,min=2,max=10,unique_name" label:"姓名"` // 必填,最大长度 10 最小长度10
       Age  int64  `validate:"min=6,max=15" label:"年龄"`                      // 最大值为15,最小值为6
       Num  string `validate:"required,len=6" label:"序号"`
    }
    
    // 注册 RegisterTagNameFunc
    validate.RegisterTagNameFunc(func(field reflect.StructField) string {
       name := strings.SplitN(field.Tag.Get("label"), ",", 2)[0]
       if name == "-" {
          return ""
       }
    
       return name
    })

    执行结果如下:





    本文为原创文章,请尊重辛勤劳动,如需转载,请保留本文地址
    http://www.findme.wang/blog/detail/id/733.html

    若您感觉本站文章不错,读后有收获,不妨赞助一下?

    我要赞助

    您还可以分享给朋友哦

    更多
    顶
    4
    踩
    1
    • 下一篇: influxdb的使用(一)
    • 查看评论
    • 正在加载中...
    • 留言
    • 亲,您还没有登录,登录后留言不需要审核哦!
      可以使用如下方式登录哦!
  • CSDN | 新浪微博 | github | 关于我们 | 我要留言 | 友链申请
  • 豫ICP备18038193号    Copyright ©lidequan All Rights Reserved