golang常用庫:欄位引數驗證庫-validator使用
阿新 • • 發佈:2020-10-16
[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