本文主题:基于asaskevich/govalidator实现Golang数据校验

小慢哥的原创文章,欢迎转载


目录

▪ 一. asaskevich/govalidator介绍

▪ 二. 字符串匹配

▪ 三. struct元素匹配

▪ 四. struct元素可选验证

▪ 五. struct嵌套校验

▪ 六. 无法实现嵌套的可选校验

▪ 七. 个人最佳实践

▪ 八. 其他功能

▪ 附录1. 字符串合法性校验

▪ 附录2. struct元素校验项

▪ 附录3. 数据特征匹配

▪ 附录4. 类型转换

▪ 附录5. 裁剪、处理、填充、遍历等


一. asaskevich/govalidator介绍

godoc里可以搜到若干相似的第三方数据校验模块,但笔者推荐使用asaskevich/govalidator,原因:

▷ star最多、持续更新发布

▷ 功能完善、使用便利

▷ 丰富的字符串校验、数据匹配、裁剪拼接处理等

▷ 支持struct元素合法性校验,并且支持嵌套检查

▷ 源码值得学习,就是一个百宝箱

// 下载
go get github.com/asaskevich/govalidator

注意:查看使用方法到github,查看支持的函数列表到godoc

https://github.com/asaskevich/govalidator
https://godoc.org/github.com/asaskevich/govalidator

二. 字符串匹配

govalidator支持非常多种字符串匹配,先贴上一个简单例子

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

完整的可用校验方法列表详见本文附录1、3


三. 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元素只支持部分常用的校验,详见本文附录2

▪ struct元素必须是导出型,也就是必须大写字母开头,govalidator才会去理会

▪ struct元素匹配较为智能,比如range(min|max)不仅支持string也支持int类型


四. struct元素可选验证

govalidator有一个bool类型的全局变量,可通过函数govalidator.SetFieldsRequiredByDefault()进行设置:

▷ 当设置为true时,如果没有定义valid tag,则会提示错误

▷ 当设置为false时,如果没有定义valid tag,不会提示错误。默认值就是false

另外,valid tag里,可以通过显式设置方式更细颗粒度地控制:当遇到zero value时是需要验证还是提示错误。此设置可以覆盖SetFieldsRequiredByDefault()。所以,valid tag有如下几种写法

`valid:""` // 等同于空tag,即``
`valid:"-"`
`valid:","`
`valid:",optional`
`valid:",required`

接下来,分别测试:假设一个struct元素的值为空字符""(即zero value)

▷ govalidator.SetFieldsRequiredByDefault(true)

`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

▷ govalidator.SetFieldsRequiredByDefault(false)

`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

继续测试,当struct元素的值为不合法的ipv4地址字符串(非空字符串),如"192.168.1.1.1"

▷ govalidator.SetFieldsRequiredByDefault(true)

`valid:""`    // 报错:All fields are required to at least have one validation defined
`valid:"-"` // true
`valid:","` // true
`valid:",optional` // true
`valid:",required` // true
`valid:"ipv4"` // 报错:192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,optional"` // 报错:192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,required"` // 报错:192.168.1.1.1 does not validate as ipv4

▷ govalidator.SetFieldsRequiredByDefault(false):测试效果和上述完全相同

另外,还有一个全局变量参数,通过govalidator.SetNilPtrAllowedByRequired()设置,但由于笔者尚未测试过,因此直接贴出官方解释

// 来自github
SetNilPtrAllowedByRequired causes validation to pass when struct fields marked by required are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between nil and zero value state can use this. If disabled, both nil and zero values cause validation errors. // 来自godoc
SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. The validation will still reject ptr fields in their zero value state. Example with this enabled: type exampleStruct struct {
Name *string `valid:"required" With `Name` set to "", this will be considered invalid input and will cause a validation error. With `Name` set to nil, this will be considered valid by validation. By default this is disabled.

五. struct嵌套校验

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

package main

import (
"fmt"
"github.com/asaskevich/govalidator"
) 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

注意:可以给Foo设置一个元素名,但也必须是大写字母开头,比如

MyFoo Foo `valid:",required"`    // 正确,可以读取到
myFoo Foo `valid:",required"` // 错误,无法读取到

六. 无法实现嵌套的可选校验

无法实现以嵌套为颗粒度的可选校验,比如下面这样是没有效果的

type bar struct {
X string `valid:"ipv4"`
Foo `valid:",optional"` // 不可行
}

因为上面代码实际会被转换为这样

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

这就导致没有办法实现Foo全校验或者全不校验


七. 个人最佳实践

建议全部显式配置校验,因为使用隐式一旦配置有误,难以及时发现

▷ govalidator.SetFieldsRequiredByDefault(true)

▷ valid tag写法:带上required,例如:

想做验证使用`valid:ipv4,required`
不想做验证使用`valid:",required"`

八. 其他功能

govalidator的校验功能还支持自定义tag与自定义校验函数,由于笔者尚未深度实践过,因此请参考官方github文档。

govalidator除了支持校验,还支持较为丰富的字符串裁剪、处理、正则等功能,以及若干类型转换功能,详见本文附录4、5(本文相比godoc和官网文档进行了更为细致的分类)。但笔者不推荐直接使用这些裁剪、处理、正则功能,因为实际上就是做了一层封装和一些细节处理,并不复杂,但可以学习。

笔者认为在使用govalidator的任何功能时,先看看源码,这是一个大而全的源码宝库,非常值得学习和借鉴。


附录1. 字符串合法性校验

下面都是业务级别的合法校验,比如是否为IPv4格式,是否为URL

func IsBase64(str string) bool
func IsCIDR(str string) bool // 是否为合法的CIDR格式,包含了IPv4与IPv6
func IsCreditCard(str string) bool
func IsDNSName(str string) bool
func IsDataURI(str string) bool
func IsEmail(str string) bool
func IsExistingEmail(email string) bool
func IsFilePath(str string) (bool, int)
func IsHash(str string, algorithm string) bool
func IsHexcolor(str string) bool
func IsHost(str string) bool
func IsIP(str string) bool // 是否为合法的IP地址,包含了IPv4与IPv6
func IsIPv4(str string) bool
func IsIPv6(str string) bool
func IsISBN(str string, version int) bool
func IsISBN10(str string) bool
func IsISBN13(str string) bool
func IsISO3166Alpha2(str string) bool
func IsISO3166Alpha3(str string) bool
func IsISO4217(str string) bool
func IsISO693Alpha2(str string) bool
func IsISO693Alpha3b(str string) bool
func IsJSON(str string) bool // 通过json.Unmarshal()是否返回error进行判断
func IsLatitude(str string) bool
func IsLongitude(str string) bool
func IsMAC(str string) bool // 支持aa:bb:cc:dd:ee:ff,以及aabb.ccdd.eeff格式
func IsMongoID(str string) bool
func IsPort(str string) bool
func IsRFC3339(str string) bool
func IsRFC3339WithoutZone(str string) bool
func IsRGBcolor(str string) bool
func IsRequestURI(rawurl string) bool
func IsRequestURL(rawurl string) bool
func IsRsaPub(str string, params ...string) bool
func IsRsaPublicKey(str string, keylen int) bool
func IsSSN(str string) bool
func IsSemver(str string) bool
func IsTime(str string, format string) bool
func IsURL(str string) bool
func IsUUID(str string) bool // 包含UUIDv3、UUIDv4、UUIDv5
func IsUUIDv3(str string) bool
func IsUUIDv4(str string) bool
func IsUUIDv5(str string) bool

附录2. struct元素校验项

有2种,第一种是不带参数的,第二种是带参数的

▷ 第一种:不带参数(第一列表示在valid tag里怎么写,第二列表示相当于govalidator的哪个导出函数)

"email": IsEmail,
"url": IsURL,
"dialstring": IsDialString,
"requrl": IsRequestURL,
"requri": IsRequestURI,
"alpha": IsAlpha,
"utfletter": IsUTFLetter,
"alphanum": IsAlphanumeric,
"utfletternum": IsUTFLetterNumeric,
"numeric": IsNumeric,
"utfnumeric": IsUTFNumeric,
"utfdigit": IsUTFDigit,
"hexadecimal": IsHexadecimal,
"hexcolor": IsHexcolor,
"rgbcolor": IsRGBcolor,
"lowercase": IsLowerCase,
"uppercase": IsUpperCase,
"int": IsInt,
"float": IsFloat,
"null": IsNull,
"uuid": IsUUID,
"uuidv3": IsUUIDv3,
"uuidv4": IsUUIDv4,
"uuidv5": IsUUIDv5,
"creditcard": IsCreditCard,
"isbn10": IsISBN10,
"isbn13": IsISBN13,
"json": IsJSON,
"multibyte": IsMultibyte,
"ascii": IsASCII,
"printableascii": IsPrintableASCII,
"fullwidth": IsFullWidth,
"halfwidth": IsHalfWidth,
"variablewidth": IsVariableWidth,
"base64": IsBase64,
"datauri": IsDataURI,
"ip": IsIP,
"port": IsPort,
"ipv4": IsIPv4,
"ipv6": IsIPv6,
"dns": IsDNSName,
"host": IsHost,
"mac": IsMAC,
"latitude": IsLatitude,
"longitude": IsLongitude,
"ssn": IsSSN,
"semver": IsSemver,
"rfc3339": IsRFC3339,
"rfc3339WithoutZone": IsRFC3339WithoutZone,
"ISO3166Alpha2": IsISO3166Alpha2,
"ISO3166Alpha3": IsISO3166Alpha3,

▷ 第二种:带参数(第一列表示在valid tag里怎么写,第二列表示相当于govalidator的哪个导出函数)

"range(min|max)": Range,
"length(min|max)": ByteLength,
"runelength(min|max)": RuneLength,
"stringlength(min|max)": StringLength,
"matches(pattern)": StringMatches,
"in(string1|string2|...|stringN)": IsIn,
"rsapub(keylength)" : IsRsaPub,

附录3. 数据特征匹配

下面是非业务的数据校验,比如在一个字符串中是否包含固定字符、是否包含空白符、是否正整数

func ByteLength(str string, params ...string) bool
func Contains(str, substring string) bool
func HasLowerCase(str string) bool
func HasUpperCase(str string) bool
func HasWhitespace(str string) bool
func HasWhitespaceOnly(str string) bool
func InRange(value interface{}, left interface{}, right interface{}) bool
func InRangeFloat32(value, left, right float32) bool
func InRangeFloat64(value, left, right float64) bool
func InRangeInt(value, left, right interface{}) bool
func IsASCII(str string) bool
func IsAlpha(str string) bool
func IsAlphanumeric(str string) bool
func IsByteLength(str string, min, max int) bool
func IsDialString(str string) bool
func IsDivisibleBy(str, num string) bool
func IsFloat(str string) bool
func IsFullWidth(str string) bool
func IsHalfWidth(str string) bool
func IsHexadecimal(str string) bool
func IsIn(str string, params ...string) bool
func IsInt(str string) bool
func IsLowerCase(str string) bool
func IsMultibyte(str string) bool
func IsNatural(value float64) bool
func IsNegative(value float64) bool
func IsNonNegative(value float64) bool // >=0
func IsNonPositive(value float64) bool // <=0
func IsNull(str string) bool // 空字符串
func IsNumeric(str string) bool // 字符串里仅包含数字
func IsPositive(value float64) bool // 正数
func IsPrintableASCII(str string) bool
func IsUTFDigit(str string) bool
func IsUTFLetter(str string) bool
func IsUTFLetterNumeric(str string) bool
func IsUTFNumeric(str string) bool
func IsUpperCase(str string) bool
func IsVariableWidth(str string) bool
func IsWhole(value float64) bool // 整数
func Matches(str, pattern string) bool // 正则匹配
func Range(str string, params ...string) bool // 字符串长度,params的string会转换成float64然后调用InRange(),主要是用于struct tag的range(min|max)
func RuneLength(str string, params ...string) bool // alias for StringLength
func Sign(value float64) float64 // 如果大于0则返回1,等于0返回0,小于0返回-1
func StringLength(str string, params ...string) bool // 字符串长度在指定范围内(视为utf8)
func StringMatches(s string, params ...string) bool // 正则匹配,等同于Matches(),主要是用于struct tag的range(min|max)

附录4. 类型转换

func ToBoolean(str string) (bool, error)
func ToFloat(str string) (float64, error)
func ToInt(value interface{}) (res int64, err error)
func ToString(obj interface{}) string
func ToJSON(obj interface{}) (string, error)
func NormalizeEmail(str string) (string, error) // 输出规范化的电子邮件格式

附录5. 裁剪、处理、填充、遍历等

func Abs(value float64) float64 // 获得绝对值
func BlackList(str, chars string) string // 从字符串中移除指定字符
func CamelCaseToUnderscore(str string) string // 将驼峰拼写法转换为下划线分割写法,如MyFunc => my_func
func Count(array []interface{}, iterator ConditionIterator) int // 通过自定义ConditionIterator(实现了迭代器)来实现判断count
func Each(array []interface{}, iterator Iterator) // 通过自定义Iterator(实现了迭代器)来实现操作,不做任何返回,自行处理,比如打印一些东西
func Filter(array []interface{}, iterator ConditionIterator) []interface{} // 通过自定义ConditionIterator(实现了迭代器)来对[]interface{}的元素进行遍历处理
func Find(array []interface{}, iterator ConditionIterator) interface{} // 通过自定义ConditionIterator(实现了迭代器)来对[]interface{}的元素进行遍历查找,返回第一个找到的,若都没找到则返回nil
func GetLine(s string, index int) (string, error) // 从含多行的字符串中返回指定行内容(0为第一行),res, err := valid.GetLine("aa\nbb\ncc\n", 1) 返回bb
func GetLines(s string) []string // 将字符串的换行符去掉,返回由每一行组成的slice,原理就是strings.Split(s, "\n")
func LeftTrim(str, chars string) string // 若字符串最左边匹配了chars,则删除,如果chars为"",则删除前导符(空格、tab、换行符)
func Map(array []interface{}, iterator ResultIterator) []interface{}
func PadBoth(str string, padStr string, padLen int) string // 字符串首尾填充字符
func PadLeft(str string, padStr string, padLen int) string // 字符串开头填充字符
func PadRight(str string, padStr string, padLen int) string // 字符串末尾填充字符
func RemoveTags(s string) string // RemoveTags remove all tags from HTML string
func ReplacePattern(str, pattern, replace string) string // 将正则匹配到的字符用指定字符替换
func Reverse(s string) string // 字符反转
func RightTrim(str, chars string) string // 若字符串最右边匹配了chars,则删除,如果chars为"",则删除前导符(空格、tab、换行符)
func SafeFileName(str string) string // 返回安全的文件名,裁剪掉空格等符号,转小写字母等
func Trim(str, chars string) string // 就是LeftTrim+RightTrim
func Truncate(str string, length int, ending string) string
func UnderscoreToCamelCase(s string) string // 将下划线分割写法转换为驼峰拼写法
func WhiteList(str, chars string) string // 从字符串中移除非指定字符

GO学习笔记 - 数据校验的更多相关文章

  1. 一脸懵逼学习Struts数据校验以及数据回显,模型驱动,防止表单重复提交的应用。

    1:Struts2表单数据校验: (1)前台校验,也称之为客户端校验,主要是通过Javascript编程的方式进行数据的验证. (2)后台校验,也称之为服务器校验,这里指的是使用Struts2通过xm ...

  2. EXCEL 2010学习笔记 —— 数据透视表

    今天整理一下EXCEL2010 数据透视表的课程笔记,数据透视表可以对多组数据进行统计和整理,是一种基本的数据可视化工具. 记录6个方面的总结: 1.创建数据透视表 2.更改数据透视表的汇总方式 3. ...

  3. MongoDB学习day09--Mongoose数据校验

    一.Mongoose检验参数 required : 表示这个数据必须传入max: 用于 Number 类型数据, 最大值 min: 用于 Number 类型数据, 最小值 enum:枚举类型, 要求数 ...

  4. AppCan学习笔记--数据存储及listview简单应用

    AppCan AppCan开发平台简介 AppCan是Hybrid App开发框架即混合开发框架,有官方提供底层功能使用API HTML5和JavaScript只是作为一种解析语言,真正调用的都是Na ...

  5. MySQL学习笔记 -- 数据表的基本操作

    数据库是一个可以存放数据库对象的容器,数据库对象包括:表.视图.存储过程.函数.触发器.事件.其中,表是数据库最基本的元素,是其他数据库对象的前提条件. 表中的一列称为一个字段,一行称为一条记录. 1 ...

  6. C#学习笔记-数据的传递以及ToolStripProgressBar

    代码: 方法一:窗体的代码-->可以直接通过预设的Click事件来实现控制进度条. public partial class Form1 : Form { public Form1() { In ...

  7. C#学习笔记-数据的传递(公共变量)以及Dictionary

    看的代码越多,写的代码越多,就越是享受这些字符,终于渐渐懂得了那种传闻中的成就感,特别是自己从看不懂然后一步一步学,一个代码一个代码地敲,最后哪怕只是完成了一个小功能,也都是特别自豪的!这种自豪不用告 ...

  8. MongoDB学习笔记(数据操作)

    1.  批量插入:     以数组的方式一次插入多个文档可以在单次TCP请求中完成,避免了多次请求中的额外开销.就数据传输量而言,批量插入的数据中仅包含一份消息头,而多次单条插入则会在每次插入数据时封 ...

  9. Scikit-Learn模块学习笔记——数据预处理模块preprocessing

    preprocessing 模块提供了数据预处理函数和预处理类,预处理类主要是为了方便添加到 pipeline 过程中. 数据标准化 标准化预处理函数: preprocessing.scale(X, ...

随机推荐

  1. serf 中去中心化系统的原理和实现

    原文:https://www.infoq.cn/article/principle-and-impleme-of-de-centering-system-in-serf serf 是出自 Hashic ...

  2. 用Python在Excel里画出蒙娜丽莎

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 麦麦麦造 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  3. SSM定时任务(spring3.0)

    SSM定时任务主要分为两部分 1.applicationContext.xml配置文件设置 设置如下: 在xmlns中添加:xmlns:task="http://www.springfram ...

  4. 往element 模块里面渲染数据

    <template> <div class="hello"> <section class="el-container"> ...

  5. Android 网络加载通用Loading

    为了用户体验用好,App在网络请求时通常都会显示个进度显示圈圈,提示用户耐心等待,最脍炙人口的莫过于登录啦. 接下来我们就通过Dialog来实现,然后在BaseActivity,BaseFragmen ...

  6. SQL学习_SELECT

    查询列: SQL:SELECT name FROM heros 多列查询: SQL:SELECT name, hp_max, mp_max, attack_max, defense_max FROM ...

  7. Java垃圾收集器——Serial,Parallel,CMS,G1收集器概述

    1.概述 Java应用启动的时候,除了配置Xms以及Xmx参数(Xmx:InitialHeapSize, Xms:MaxHeapSize),还需要选择合适的垃圾收集器. 截止Jdk1.8,共提供了7款 ...

  8. Shell命令-用户用户组管理之userdel、groupadd

    文件及内容处理 - userdel.groupadd 1. userdel:删除用户 userdel命令的功能说明 userdel 命令用于删除用户帐号.userdel 可删除用户帐号与相关的文件.若 ...

  9. Vysor

    官网:http://www.vysor.io/ Vysor用 PC远程控制投影安卓手机/平板工具 Vysor 是一个免费的google浏览器插件. 它可以让你在pc上控制你的Android手机.平板等 ...

  10. 7.5 高级数据源---Kafka

    一.Kafka简介 Kafka是一种高吞吐量的分布式发布订阅消息系统,用户通过Kafka系统可以发布大量的消息,同时也能实时订阅消费消息.Kafka可以同时满足在线实时处理和批量离线处理. 在公司的大 ...