溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

GO學(xué)習(xí)筆記 - 數(shù)據(jù)校驗(yàn)

發(fā)布時(shí)間:2020-07-19 14:58:04 來(lái)源:網(wǎng)絡(luò) 閱讀:1139 作者:小慢哥 欄目:編程語(yǔ)言

GO學(xué)習(xí)筆記 - 數(shù)據(jù)校驗(yàn)

本文主題:基于asaskevich/govalidator實(shí)現(xiàn)Golang數(shù)據(jù)校驗(yàn)

小慢哥的原創(chuàng)文章,歡迎轉(zhuǎn)載


目錄

? 一. asaskevich/govalidator介紹
? 二. 字符串匹配
? 三. struct元素匹配
? 四. struct元素可選驗(yàn)證
? 五. struct嵌套校驗(yàn)
? 六. 無(wú)法實(shí)現(xiàn)嵌套的可選校驗(yàn)
? 七. 個(gè)人最佳實(shí)踐
? 八. 其他功能
? 附錄1. 字符串合法性校驗(yàn)
? 附錄2. struct元素校驗(yàn)項(xiàng)
? 附錄3. 數(shù)據(jù)特征匹配
? 附錄4. 類型轉(zhuǎn)換
? 附錄5. 裁剪、處理、填充、遍歷等


一. asaskevich/govalidator介紹

godoc里可以搜到若干相似的第三方數(shù)據(jù)校驗(yàn)?zāi)K,但筆者推薦使用asaskevich/govalidator,原因:

? star最多、持續(xù)更新發(fā)布
? 功能完善、使用便利
? 豐富的字符串校驗(yàn)、數(shù)據(jù)匹配、裁剪拼接處理等
? 支持struct元素合法性校驗(yàn),并且支持嵌套檢查
? 源碼值得學(xué)習(xí),就是一個(gè)百寶箱

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

注意:查看使用方法到github,查看支持的函數(shù)列表到godoc

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

二. 字符串匹配

govalidator支持非常多種字符串匹配,先貼上一個(gè)簡(jiǎn)單例子

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

    // 判斷數(shù)字是否在指定范圍內(nèi)
    dig := 101    // string類型也可以用
    fmt.Println(govalidator.InRange(dig, 0, 100)) // false
}

輸出

true
false
false

完整的可用校驗(yàn)方法列表詳見(jiàn)本文附錄1、3


三. struct元素匹配

govalidator專門(mén)提供了一個(gè)函數(shù),用于校驗(yàn)struct的元素

govalidator.ValidateStruct()

簡(jiǎn)單例子

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元素只支持部分常用的校驗(yàn),詳見(jiàn)本文附錄2
? struct元素必須是導(dǎo)出型,也就是必須大寫(xiě)字母開(kāi)頭,govalidator才會(huì)去理會(huì)
? struct元素匹配較為智能,比如range(min|max)不僅支持string也支持int類型


四. struct元素可選驗(yàn)證

govalidator有一個(gè)bool類型的全局變量,可通過(guò)函數(shù)govalidator.SetFieldsRequiredByDefault()進(jìn)行設(shè)置:

? 當(dāng)設(shè)置為true時(shí),如果沒(méi)有定義valid tag,則會(huì)提示錯(cuò)誤
? 當(dāng)設(shè)置為false時(shí),如果沒(méi)有定義valid tag,不會(huì)提示錯(cuò)誤。默認(rèn)值就是false

另外,valid tag里,可以通過(guò)顯式設(shè)置方式更細(xì)顆粒度地控制:當(dāng)遇到zero value時(shí)是需要驗(yàn)證還是提示錯(cuò)誤。此設(shè)置可以覆蓋SetFieldsRequiredByDefault()。所以,valid tag有如下幾種寫(xiě)法

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

接下來(lái),分別測(cè)試:假設(shè)一個(gè)struct元素的值為空字符""(即zero value)

? govalidator.SetFieldsRequiredByDefault(true)

`valid:""`    // 報(bào)錯(cuò):All fields are required to at least have one validation defined
`valid:"-"`    // true
`valid:","`    // 報(bào)錯(cuò):Missing required field
`valid:",optional`    // true
`valid:",required`    // 報(bào)錯(cuò):non zero value required
`valid:"ipv4"`    // 報(bào)錯(cuò):Missing required field
`valid:"ipv4,optional"`    // true
`valid:"ipv4,required"`    // 報(bào)錯(cuò):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"`    // 報(bào)錯(cuò):non zero value required

繼續(xù)測(cè)試,當(dāng)struct元素的值為不合法的ipv4地址字符串(非空字符串),如"192.168.1.1.1"

? govalidator.SetFieldsRequiredByDefault(true)

`valid:""`    // 報(bào)錯(cuò):All fields are required to at least have one validation defined
`valid:"-"`    // true
`valid:","`    // true
`valid:",optional`    // true
`valid:",required`    // true
`valid:"ipv4"`    // 報(bào)錯(cuò):192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,optional"`    // 報(bào)錯(cuò):192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,required"`    // 報(bào)錯(cuò):192.168.1.1.1 does not validate as ipv4

? govalidator.SetFieldsRequiredByDefault(false):測(cè)試效果和上述完全相同

另外,還有一個(gè)全局變量參數(shù),通過(guò)govalidator.SetNilPtrAllowedByRequired()設(shè)置,但由于筆者尚未測(cè)試過(guò),因此直接貼出官方解釋

// 來(lái)自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.

// 來(lái)自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嵌套校驗(yàn)

嵌套元素名必須是導(dǎo)出型,也就是大寫(xiě)字母開(kāi)頭,舉例

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設(shè)置一個(gè)元素名,但也必須是大寫(xiě)字母開(kāi)頭,比如

MyFoo Foo `valid:",required"`    // 正確,可以讀取到
myFoo Foo `valid:",required"`    // 錯(cuò)誤,無(wú)法讀取到

六. 無(wú)法實(shí)現(xiàn)嵌套的可選校驗(yàn)

無(wú)法實(shí)現(xiàn)以嵌套為顆粒度的可選校驗(yàn),比如下面這樣是沒(méi)有效果的

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

因?yàn)樯厦娲a實(shí)際會(huì)被轉(zhuǎn)換為這樣

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

這就導(dǎo)致沒(méi)有辦法實(shí)現(xiàn)Foo全校驗(yàn)或者全不校驗(yàn)


七. 個(gè)人最佳實(shí)踐

建議全部顯式配置校驗(yàn),因?yàn)槭褂秒[式一旦配置有誤,難以及時(shí)發(fā)現(xiàn)

? govalidator.SetFieldsRequiredByDefault(true)
? valid tag寫(xiě)法:帶上required,例如:

想做驗(yàn)證使用`valid:ipv4,required`
不想做驗(yàn)證使用`valid:",required"`

八. 其他功能

govalidator的校驗(yàn)功能還支持自定義tag與自定義校驗(yàn)函數(shù),由于筆者尚未深度實(shí)踐過(guò),因此請(qǐng)參考官方github文檔。

govalidator除了支持校驗(yàn),還支持較為豐富的字符串裁剪、處理、正則等功能,以及若干類型轉(zhuǎn)換功能,詳見(jiàn)本文附錄4、5(本文相比godoc和官網(wǎng)文檔進(jìn)行了更為細(xì)致的分類)。但筆者不推薦直接使用這些裁剪、處理、正則功能,因?yàn)閷?shí)際上就是做了一層封裝和一些細(xì)節(jié)處理,并不復(fù)雜,但可以學(xué)習(xí)。

筆者認(rèn)為在使用govalidator的任何功能時(shí),先看看源碼,這是一個(gè)大而全的源碼寶庫(kù),非常值得學(xué)習(xí)和借鑒。


附錄1. 字符串合法性校驗(yàn)

下面都是業(yè)務(wù)級(jí)別的合法校驗(yàn),比如是否為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    // 通過(guò)json.Unmarshal()是否返回error進(jìn)行判斷
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元素校驗(yàn)項(xiàng)

有2種,第一種是不帶參數(shù)的,第二種是帶參數(shù)的

? 第一種:不帶參數(shù)(第一列表示在valid tag里怎么寫(xiě),第二列表示相當(dāng)于govalidator的哪個(gè)導(dǎo)出函數(shù))

"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,

? 第二種:帶參數(shù)(第一列表示在valid tag里怎么寫(xiě),第二列表示相當(dāng)于govalidator的哪個(gè)導(dǎo)出函數(shù))

"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. 數(shù)據(jù)特征匹配

下面是非業(yè)務(wù)的數(shù)據(jù)校驗(yàn),比如在一個(gè)字符串中是否包含固定字符、是否包含空白符、是否正整數(shù)

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 // 字符串里僅包含數(shù)字
func IsPositive(value float64) bool // 正數(shù)
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 // 整數(shù)
func Matches(str, pattern string) bool // 正則匹配
func Range(str string, params ...string) bool // 字符串長(zhǎng)度,params的string會(huì)轉(zhuǎn)換成float64然后調(diào)用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 // 字符串長(zhǎng)度在指定范圍內(nèi)(視為utf8)
func StringMatches(s string, params ...string) bool // 正則匹配,等同于Matches(),主要是用于struct tag的range(min|max)

附錄4. 類型轉(zhuǎn)換

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)    // 輸出規(guī)范化的電子郵件格式

附錄5. 裁剪、處理、填充、遍歷等

func Abs(value float64) float64 // 獲得絕對(duì)值
func BlackList(str, chars string) string // 從字符串中移除指定字符
func CamelCaseToUnderscore(str string) string // 將駝峰拼寫(xiě)法轉(zhuǎn)換為下劃線分割寫(xiě)法,如MyFunc => my_func
func Count(array []interface{}, iterator ConditionIterator) int // 通過(guò)自定義ConditionIterator(實(shí)現(xiàn)了迭代器)來(lái)實(shí)現(xiàn)判斷count
func Each(array []interface{}, iterator Iterator) // 通過(guò)自定義Iterator(實(shí)現(xiàn)了迭代器)來(lái)實(shí)現(xiàn)操作,不做任何返回,自行處理,比如打印一些東西
func Filter(array []interface{}, iterator ConditionIterator) []interface{} // 通過(guò)自定義ConditionIterator(實(shí)現(xiàn)了迭代器)來(lái)對(duì)[]interface{}的元素進(jìn)行遍歷處理
func Find(array []interface{}, iterator ConditionIterator) interface{} // 通過(guò)自定義ConditionIterator(實(shí)現(xiàn)了迭代器)來(lái)對(duì)[]interface{}的元素進(jìn)行遍歷查找,返回第一個(gè)找到的,若都沒(méi)找到則返回nil
func GetLine(s string, index int) (string, error) // 從含多行的字符串中返回指定行內(nèi)容(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為"",則刪除前導(dǎo)符(空格、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 // 字符串開(kāi)頭填充字符
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 // 字符反轉(zhuǎn)
func RightTrim(str, chars string) string // 若字符串最右邊匹配了chars,則刪除,如果chars為"",則刪除前導(dǎo)符(空格、tab、換行符)
func SafeFileName(str string) string // 返回安全的文件名,裁剪掉空格等符號(hào),轉(zhuǎn)小寫(xiě)字母等
func Trim(str, chars string) string // 就是LeftTrim+RightTrim
func Truncate(str string, length int, ending string) string
func UnderscoreToCamelCase(s string) string // 將下劃線分割寫(xiě)法轉(zhuǎn)換為駝峰拼寫(xiě)法
func WhiteList(str, chars string) string // 從字符串中移除非指定字符
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI