GO學習筆記 - 資料校驗

GO學習筆記 - 資料校驗

本文主題:基於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/govalidatorhttps://godoc。org/github。com/asaskevich/govalidator

二。 字串匹配

govalidator支援非常多種字串匹配,先貼上一個簡單例子

package mainimport ( “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}

輸出

truefalsefalse

完整的可用校驗方法列表詳見本文附錄1、3

三。 struct元素匹配

govalidator專門提供了一個函式,用於校驗struct的元素

govalidator。ValidateStruct()

簡單例子

package mainimport ( “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()設定,但由於筆者尚未測試過,因此直接貼出官方解釋

// 來自githubSetNilPtrAllowedByRequired 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。// 來自godocSetNilPtrAllowedByRequired 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 mainimport ( “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 ipv4false

注意:可以給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) boolfunc IsCIDR(str string) bool // 是否為合法的CIDR格式,包含了IPv4與IPv6func IsCreditCard(str string) boolfunc IsDNSName(str string) boolfunc IsDataURI(str string) boolfunc IsEmail(str string) boolfunc IsExistingEmail(email string) boolfunc IsFilePath(str string) (bool, int)func IsHash(str string, algorithm string) boolfunc IsHexcolor(str string) boolfunc IsHost(str string) boolfunc IsIP(str string) bool // 是否為合法的IP地址,包含了IPv4與IPv6func IsIPv4(str string) boolfunc IsIPv6(str string) boolfunc IsISBN(str string, version int) boolfunc IsISBN10(str string) boolfunc IsISBN13(str string) boolfunc IsISO3166Alpha2(str string) boolfunc IsISO3166Alpha3(str string) boolfunc IsISO4217(str string) boolfunc IsISO693Alpha2(str string) boolfunc IsISO693Alpha3b(str string) boolfunc IsJSON(str string) bool // 透過json。Unmarshal()是否返回error進行判斷func IsLatitude(str string) boolfunc IsLongitude(str string) boolfunc IsMAC(str string) bool // 支援aa:bb:cc:dd:ee:ff,以及aabb。ccdd。eeff格式func IsMongoID(str string) boolfunc IsPort(str string) boolfunc IsRFC3339(str string) boolfunc IsRFC3339WithoutZone(str string) boolfunc IsRGBcolor(str string) boolfunc IsRequestURI(rawurl string) boolfunc IsRequestURL(rawurl string) boolfunc IsRsaPub(str string, params 。。。string) boolfunc IsRsaPublicKey(str string, keylen int) boolfunc IsSSN(str string) boolfunc IsSemver(str string) boolfunc IsTime(str string, format string) boolfunc IsURL(str string) boolfunc IsUUID(str string) bool // 包含UUIDv3、UUIDv4、UUIDv5func IsUUIDv3(str string) boolfunc IsUUIDv4(str string) boolfunc 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) boolfunc Contains(str, substring string) boolfunc HasLowerCase(str string) boolfunc HasUpperCase(str string) boolfunc HasWhitespace(str string) boolfunc HasWhitespaceOnly(str string) boolfunc InRange(value interface{}, left interface{}, right interface{}) boolfunc InRangeFloat32(value, left, right float32) boolfunc InRangeFloat64(value, left, right float64) boolfunc InRangeInt(value, left, right interface{}) boolfunc IsASCII(str string) boolfunc IsAlpha(str string) boolfunc IsAlphanumeric(str string) boolfunc IsByteLength(str string, min, max int) boolfunc IsDialString(str string) boolfunc IsDivisibleBy(str, num string) boolfunc IsFloat(str string) boolfunc IsFullWidth(str string) boolfunc IsHalfWidth(str string) boolfunc IsHexadecimal(str string) boolfunc IsIn(str string, params 。。。string) boolfunc IsInt(str string) boolfunc IsLowerCase(str string) boolfunc IsMultibyte(str string) boolfunc IsNatural(value float64) boolfunc IsNegative(value float64) boolfunc IsNonNegative(value float64) bool // >=0func IsNonPositive(value float64) bool // <=0func IsNull(str string) bool // 空字串func IsNumeric(str string) bool // 字串裡僅包含數字func IsPositive(value float64) bool // 正數func IsPrintableASCII(str string) boolfunc IsUTFDigit(str string) boolfunc IsUTFLetter(str string) boolfunc IsUTFLetterNumeric(str string) boolfunc IsUTFNumeric(str string) boolfunc IsUpperCase(str string) boolfunc IsVariableWidth(str string) boolfunc 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 StringLengthfunc Sign(value float64) float64 // 如果大於0則返回1,等於0返回0,小於0返回-1func 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{}) stringfunc 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_funcfunc Count(array []interface{}, iterator ConditionIterator) int // 透過自定義ConditionIterator(實現了迭代器)來實現判斷countfunc Each(array []interface{}, iterator Iterator) // 透過自定義Iterator(實現了迭代器)來實現操作,不做任何返回,自行處理,比如列印一些東西func Filter(array []interface{}, iterator ConditionIterator) []interface{} // 透過自定義ConditionIterator(實現了迭代器)來對[]interface{}的元素進行遍歷處理func Find(array []interface{}, iterator ConditionIterator) interface{} // 透過自定義ConditionIterator(實現了迭代器)來對[]interface{}的元素進行遍歷查詢,返回第一個找到的,若都沒找到則返回nilfunc GetLine(s string, index int) (string, error) // 從含多行的字串中返回指定行內容(0為第一行),res, err := valid。GetLine(“aa\nbb\ncc\n”, 1) 返回bbfunc 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 stringfunc 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+RightTrimfunc Truncate(str string, length int, ending string) stringfunc UnderscoreToCamelCase(s string) string // 將下劃線分割寫法轉換為駝峰拼寫法func WhiteList(str, chars string) string // 從字串中移除非指定字元