1. 程式人生 > >golang常用庫:欄位引數驗證庫-validator使用

golang常用庫:欄位引數驗證庫-validator使用

[golang常用庫:gorilla/mux-http路由庫使用](https://www.cnblogs.com/jiujuan/p/12768907.html) [golang常用庫:配置檔案解析庫-viper使用](https://www.cnblogs.com/jiujuan/p/13799976.html) [golang常用庫:操作資料庫的orm框架-gorm基本使用](https://www.cnblogs.com/jiujuan/p/12676195.html) [golang常用庫:欄位引數驗證庫-validator使用](https://www.cnblogs.com/jiujuan/p/13823864.html) ## 一、背景 在平常開發中,特別是在web應用開發中,為了驗證輸入欄位的合法性,都會做一些驗證操作。比如對使用者提交的表單欄位進行驗證,或者對請求的API介面欄位進行驗證,驗證欄位的合法性,保證輸入欄位值的安全,防止使用者的惡意請求。 一般的做法是用正則表示式,一個欄位一個欄位的進行驗證。一個一個欄位驗證的話,寫起來比較繁瑣。那有沒更好的方法,進行欄位的合法性驗證?有, 這就是下面要介紹的 [validator](https://github.com/go-playground/validator) 這個驗證元件。 程式碼地址: [https://github.com/go-playground/validator](https://github.com/go-playground/validator) 文件地址: [https://github.com/go-playground/validator/blob/master/README.md](https://github.com/go-playground/validator/blob/master/README.md) ## 二、功能介紹 這個驗證包 [github.com/go-playground/validator](https://github.com/go-playground/validator) 驗證功能非常多。 ### 標記之間特殊符號說明 - 逗號( `, `):把多個驗證標記隔開。`注意`:隔開逗號之間不能有空格, `validate:"lt=0,gt=100"`,逗號那裡不能有空格,否則panic - 橫線( `-` ):跳過該欄位不驗證 - 豎線( `|` ):使用多個驗證標記,但是隻需滿足其中一個即可 - required:表示該欄位值必輸設定,且不能為預設值 - omitempty:如果欄位未設定,則忽略它 ### 範圍比較驗證 >doc: https://github.com/go-playground/validator/blob/master/README.md#comparisons 範圍驗證: 切片、陣列和map、字串,驗證其長度;數值,驗證大小範圍 - lte:小於等於引數值,`validate:"lte=3"` (小於等於3) - gte:大於等於引數值,`validate:"lte=0,gte=120"` (大於等於0小於等於120) - lt:小於引數值,`validate:"lt=3"` (小於3) - gt:大於引數值,`validate:"lt=0,gt=120"` (大於0小於120) - len:等於引數值,`validate:"len=2"` - max:大於等於引數值,`validate:"max=2"` (大於等於2) - min:小於等於引數值,`validate:"min=2,max=10"` (大於等於2小於等於10) - ne:不等於,`validate:"ne=2"` (不等於2) - oneof:只能是列舉出的值其中一個,這些值必須是數值或字串,以空格分隔,如果字串中有空格,將字串用單引號包圍,`validate:"oneof=red green"` 例子: ```go type User struct { Name string `json:"name" validate:"min=0,max=35"` Age unit8 `json:"age" validate:"lte=0,gte=90"` } ``` 更多功能請參看文件 [validator comparisons doc](https://github.com/go-playground/validator/blob/master/README.md#comparisons) ### 字串驗證 >doc: https://github.com/go-playground/validator/blob/master/README.md#strings - contains:包含引數子串,`validate:"contains=tom"` (欄位的字串值包含tom) - excludes:包含引數子串,`validate:"excludes=tom"` (欄位的字串值不包含tom) - startswith:以引數子串為字首,`validate:"startswith=golang"` - endswith:以引數子串為字尾,`validate:"startswith=world"` 例子: ```go type User struct { Name string `validate:"contains=tom"` Age int `validate:"min=1"` } ``` 更多功能請參看文件 [validator strings doc](https://github.com/go-playground/validator/blob/master/README.md#strings) ### 欄位驗證 > doc: https://github.com/go-playground/validator/blob/master/README.md#fields - eqcsfield:跨不同結構體欄位驗證,比如說 Struct1 Filed1,與結構體Struct2 Field2相等, ```go type Struct1 struct { Field1 string `validate:eqcsfield=Struct2.Field2` Struct2 struct { Field2 string } } ``` - necsfield:跨不同結構體欄位不相等 - eqfield:同一結構體欄位驗證相等,最常見的就是輸入2次密碼驗證 ```go type User struct { Name string `validate:"lte=4"` Age int `validate:"min=20"` Password string `validate:"min=10"` Password2 string `validate:"eqfield=Password"` } ``` - nefield:同一結構體欄位驗證不相等 ```go type User struct { Name string `validate:"lte=4"` Age int `validate:"min=20"` Password string `validate:"min=10,nefield=Name"` } ``` - gtefield:大於等於同一結構體欄位,`validate:"gtefiled=Field2"` - ltefield:小於等於同一結構體欄位 更多功能請參看文件:[validator Fields DOC](https://github.com/go-playground/validator/blob/master/README.md#fields) ### 網路驗證 >doc: https://github.com/go-playground/validator/blob/master/README.md#network - ip:欄位值是否包含有效的IP地址,`validate:"ip"` - ipv4:欄位值是否包含有效的ipv4地址,`validate:"ipv4"` - ipv6:欄位值是否包含有效的ipv6地址,`validate:"ipv6"` - uri:欄位值是否包含有效的uri,`validate:"uri"` - url:欄位值是否包含有效的uri,`validate:"url"` 更多功能請參看文件:[validator network DOC](https://github.com/go-playground/validator/blob/master/README.md#network) ### Format >doc: https://github.com/go-playground/validator/blob/master/README.md#format - base64:欄位值是否包含有效的base64值 更多功能請參看文件 [validator strings doc](https://github.com/go-playground/validator/blob/master/README.md#strings) ### 其他 >請參看文件: [https://github.com/go-playground/validator/blob/master/README.md#other](https://github.com/go-playground/validator/blob/master/README.md#other) ## 三、安裝 go get: >go get github.com/go-playground/validator/v10 在檔案中引用validator包: > import "github.com/go-playground/validator/v10" ## 四、validator使用 >文件:https://github.com/go-playground/validator/blob/master/README.md#examples ### 例子1:驗證單個欄位變數值 validation1.go ```go package main import ( "fmt" "github.com/go-playground/validator/v10" ) func main() { validate := validator.New() var boolTest bool err := validate.Var(boolTest, "required") if err != nil { fmt.Println(err) } var stringTest string = "" err = validate.Var(stringTest, "required") if err != nil { fmt.Println(err) } var emailTest string = "[email protected]" err = validate.Var(emailTest, "email") if err != nil { fmt.Println(err) } else { fmt.Println("success") // 輸出: success。 說明驗證成功 } emailTest2 := "test.126.com" errs := validate.Var(emailTest2, "required,email") if errs != nil { fmt.Println(errs) // 輸出: Key: "" Error:Field validation for "" failed on the "email" tag。驗證失敗 } fmt.Println("\r\nEnd!!") } ``` 執行輸出: >go run simple1.go >Key: '' Error:Field validation for '' failed on the 'required' tag >Key: '' Error:Field validation for '' failed on the 'required' tag >success >Key: '' Error:Field validation for '' failed on the 'email' tag > >End!! ### 例子2:驗證結構體struct >from:[struct validate](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go) validation_struct.go,這個程式還列出了效驗出錯欄位的一些資訊, ```go package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { FirstName string `validate:"required"` LastName string `validate:"required"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` Addresses []*Address `validate:"required,dive,required"` } type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } func main() { address := &Address{ Street: "Eavesdown Docks", Planet: "Persphone", Phone: "none", } user := &User{ FirstName: "Badger", LastName: "Smith", Age: 135, Email: "[email protected]", Addresses: []*Address{address}, } validate := validator.New() err := validate.Struct(user) if err != nil { fmt.Println("=== error msg ====") fmt.Println(err) if _, ok := err.(*validator.InvalidValidationError); ok { fmt.Println(err) return } fmt.Println("\r\n=========== error field info ====================") for _, err := range err.(validator.ValidationErrors) { // 列出效驗出錯欄位的資訊 fmt.Println("Namespace: ", err.Namespace()) fmt.Println("Fild: ", err.Field()) fmt.Println("StructNamespace: ", err.StructNamespace()) fmt.Println("StructField: ", err.StructField()) fmt.Println("Tag: ", err.Tag()) fmt.Println("ActualTag: ", err.ActualTag()) fmt.Println("Kind: ", err.Kind()) fmt.Println("Type: ", err.Type()) fmt.Println("Value: ", err.Value()) fmt.Println("Param: ", err.Param()) fmt.Println() } // from here you can create your own error messages in whatever language you wish return } } ``` 執行 輸出: >$ go run validation_struct.go >=== error msg ==== >Key: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tag >Key: 'User.Addresses[0].City' Error:Field validation for 'City' failed on the 'required' tag > >=========== error field info ==================== >Namespace: User.Age >Fild: Age >StructNamespace: User.Age >StructField: Age >Tag: lte >ActualTag: lte >Kind: uint8 >Type: uint8 >Value: 135 >Param: 130 > >Namespace: User.Addresses[0].City >Fild: City >StructNamespace: User.Addresses[0].City >StructField: City >Tag: required >ActualTag: required >Kind: string >Type: string >Value: >Param: 還可以給欄位加一些其他tag資訊,方面form,json的解析,如下: ```go type User struct { FirstName string `form:"firstname" json:"firstname" validate:"required"` LastName string `form:"lastname" json:"lastname" validate:"required"` Age uint8 ` form:"age" json:"age"validate:"gte=0,lte=130"` Email string ` form:"email" json:"email" validate:"required,email"` } ``` ### 使用者自定義函式驗證 使用者自定義函式驗證欄位是否合法,效驗是否正確。 ### 例子3: 通過欄位tag自定義函式 ##### validate.RegisterValidation customer_tag.go: ```go package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `form:"name" json:"name" validate:"required,CustomerValidation"` //注意:required和CustomerValidation之間不能有空格,否則panic。CustomerValidation:自定義tag-函式標籤 Age uint8 ` form:"age" json:"age" validate:"gte=0,lte=80"` //注意:gte=0和lte=80之間不能有空格,否則panic } var validate *validator.Validate func main() { validate = validator.New() validate.RegisterValidation("CustomerValidation", CustomerValidationFunc) //註冊自定義函式,前一個引數是struct裡tag自定義,後一個引數是自定義的函式 user := &User{ Name: "jimmy", Age: 86, } fmt.Println("first value: ", user) err := validate.Struct(user) if err != nil { fmt.Printf("Err(s):\n%+v\n", err) } user.Name = "tom" user.Age = 29 fmt.Println("second value: ", user) err = validate.Struct(user) if err != nil { fmt.Printf("Err(s):\n%+v\n", err) } } // 自定義函式 func CustomerValidationFunc(f1 validator.FieldLevel) bool { // f1 包含了欄位相關資訊 // f1.Field() 獲取當前欄位資訊 // f1.Param() 獲取tag對應的引數 // f1.FieldName() 獲取欄位名稱 return f1.Field().String() == "jimmy" } ``` 執行輸出: >$ go run customer.go >first value: &{jimmy 86} >Err(s): >Key: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tag >second value: &{tom 29} >Err(s): >Key: 'User.Name' Error:Field validation for 'Name' failed on the 'CustomerValidation' tag **注意*
*: 上面程式碼`user struct`定義中 ,`validate`裡的required和CustomerValidation之間不能有空格,否則執行時報panic錯誤:`panic: Undefined validation function ' CustomerValidation' on field 'Name'` ### 例子4:自定義函式-直接註冊函式1 不通過欄位tag自定義函式,直接註冊函式。 ##### RegisterStructValidation >https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go customer1.go ```go package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { FirstName string `json:firstname` LastName string `json:lastname` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` } var validate *validator.Validate func main() { validate = validator.New() validate.RegisterStructValidation(UserStructLevelValidation, User{}) user := &User{ FirstName: "", LastName: "", Age: 30, Email: "[email protected]", FavouriteColor: "#000", } err := validate.Struct(user) if err != nil { fmt.Println(err) } } func UserStructLevelValidation(sl validator.StructLevel) { user := sl.Current().Interface().(User) if len(user.FirstName) == 0 && len(user.LastName) == 0 { sl.ReportError(user.FirstName, "FirstName", "firstname", "firstname", "") sl.ReportError(user.LastName, "LastName", "lastname", "lastname", "") } } ``` 執行輸出: >
$ go run customer1.go >Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'firstname' tag >Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'lastname' tag ### 例子5:自定義函式-直接註冊函式2 ##### RegisterCustomTypeFunc >https://github.com/go-playground/validator/blob/master/_examples/custom/main.go [validate.RegisterCustomTypeFunc](https://github.com/go-playground/validator/blob/d6b17fd90bd4dd9d16a594c3035ceadc3de0193a/validator_instance.go#L241"):驗證型別的自定義函式 customer2.go: ```go package main import ( "database/sql" "database/sql/driver" "fmt" "reflect" "github.com/go-playground/validator/v10" ) type DbBackedUser struct { Name sql.NullString `validate:"required"` Age sql.NullInt64 `validate:"required"` } var validate *validator.Validate func main() { validate = validator.New() validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) // build object for validation x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} err := validate.Struct(x) if err != nil { fmt.Printf("Err(s):\n%+v\n", err) } } func ValidateValuer(field reflect.Value) interface{} { if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err == nil { return val } // handle the error how you want } return nil } ``` 執行輸出: >
$ go run customer.go >Err(s): >Key: 'DbBackedUser.Name' Error:Field validation for 'Name' failed on the 'required' tag >Key: 'DbBackedUser.Age' Error:Field validation for 'Age' failed on the 'required' tag **注意,這個函式**: [RegisterCustomTypeFunc](https://github.com/go-playground/validator/blob/d6b17fd90bd4dd9d16a594c3035ceadc3de0193a/validator_instance.go#L241"),它上面有2行註釋: >// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types >// >// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation 它是一個驗證資料型別自定義函式,NOTE:這個方法不是執行緒安全的 ## 五、參考 - [https://github.com/go-playground/validator/blob/master/README.md](https://github.com/go-playground/validator/blob/master/README.md) - [https://github.com/go-playground/validator/tree/master/_examples](https://github.com/go-playground/validator/tree/master/_e