目錄
文章目錄
- 目錄
- Validator
- Quick start
- 約束型別
- 特殊約束
- 格式約束
- 資料結構型別約束
- 范圍約束
- 字串約束
- 唯一性約束
- 跨欄位約束
- 自定義約束
- 錯誤處理
- 中文錯誤資訊
- 參考檔案
Validator
Validator 是一個 Golang 的第三方庫,用于對資料進行校驗,常用于 API 的開發中,對客戶端發出的請求資料進行嚴格校驗,防止惡意請求,
- Github:https://github.com/go-playground/validator
安裝:
go get gopkg.in/go-playground/validator.v10
使用:
import "github.com/go-playground/validator/v10"
NOTE:validator 當前最新的版本是 v10,各個版本之間有一些差異,在使用的時候要注意區分,
Quick start
validator 應用了 Golang 的 Struct Tag 和 Reflect 機制,基本思想是:在 Struct Tag 中為不同的欄位定義各自型別的約束,然后通過 Reflect 獲取這些約束的型別資訊并在校驗器中進行資料校驗,如下例:
package main
import (
"fmt"
"gopkg.in/go-playground/validator.v10"
)
type User struct {
Name string `validate:"min=6,max=10"`
Age int `validate:"min=1,max=100"`
}
func main() {
validate := validator.New()
u1 := User{Name: "fanguiju", Age: 18}
err := validate.Struct(u1)
fmt.Println(err)
u2 := User{Name: "fgj", Age: 101}
err = validate.Struct(u2)
fmt.Println(err)
}
上述例子中,我們定義了結構體 User,有 Name 和 Age 成員,通過 validator 的 min 和 max 約束,分別約束了 Name 的字串長度 [6, 10],Age 的數字范圍為 [1,100],
使用 validator 的第一步需要 New(構造)一個 “校驗器”,然后呼叫其 Struct 方法對結構體實體進行校驗,如果滿足約束則回傳 nil,否則回傳相應的錯誤資訊,
約束型別
特殊約束
- -:跳過該欄位,不檢驗;
- |:使用多個約束,只需要滿足其中一個,例如:rgb | rgba;
- required:必選約束,不能為默認值;
- omitempty:如果欄位未設定,則忽略它,
格式約束
- email:限制欄位必須是郵件格式,
- url:限制欄位必須是 URL 格式,
- uri:限制欄位必須是 URI 格式,
- ip、ipv4、ipv6:限制欄位必須是 IP 格式,
- uuid:限制欄位必須是 UUID 格式,
- datetime:限制欄位必須是 Datatime 格式,
- json:字串值是否為有效的 JSON,
- file:符串值是否包含有效的檔案路徑,以及該檔案是否存在于計算機上,
資料結構型別約束
- structonly:僅驗證結構體,不驗證任何結構體欄位,Struct:
validate:"structonly", - nostructlevel:不運行任何結構體級別的驗證,Struct:
validate:"nostructlevel", - dive:向下延伸驗證,多層向下需要多個 dive 標記,
[][]string:validate:"gt=0,dive,len=1,dive,required", - dive Keys & EndKeys:與 dive 同時使用,用于對 Map 物件的鍵的和值的驗證,keys 為鍵,endkeys 為值,map[string]string:
validate:"gt=0,dive,keys,eq=1\|eq=2,endkeys,required",
范圍約束
- numeric 約束:約束數值范圍;
- String 約束:約束字串長度;
- Slice、Array、Map 約束:約束其元素的個數,
Tags:
- len:長度等于引數值,例如:len=10;
- eq:數值等于引數值,與 len 不同,對于 String:eq 約束字串本身的值,而 len 則約束字串長度,例如:eq=10;
- max:數值小于等于引數值,
- min:數值大于等于引數值,
- ne:不等于引數值,
- gt:大于引數值,
- gte:大于等于引數值,
- lt:小于引數值,
- lte:小于等于引數值,
- oneof:只能是列舉值中的一個,這些值必須是數值或字串,以空格分隔,如果字串中有空格,則使用單引號包圍,例如:oneof=red green,
字串約束
下面列舉常用的字串約束:
- contains=:包含引數子串,例如:contains=email;
- containsany:包含引數中任意的 UNICODE 字符,例如:containsany=abcd;
- containsrune:包含引數表示的 rune 字符,例如:containsrune=?;
- excludes:不包含引數子串,例如:excludes=email;
- excludesall:不包含引數中任意的 UNICODE 字符,例如:excludesall=abcd;
- excludesrune:不包含引數表示的 rune 字符,例如:excludesrune=?;
- startswith:以引數子串為前綴,例如:startswith=hello;
- endswith:以引數子串為后綴,例如:endswith=bye,
- numeric:限制字串值只包含基本的數值,
唯一性約束
唯一性(unique)約束,對不同型別的處理如下:
- 對于 Slice、Array,unique 約束沒有重復的元素;
- 對于 Map,unique 約束沒有重復的值;
- 對于元素型別為結構體的 Slice,unique 約束結構體實體的某個欄位不重復,通過 unqiue=field 可以指定這個欄位名,
跨欄位約束
validator 允許定義跨欄位的約束,即:約束某個欄位與其他欄位之間的關系,這種約束實際上分為兩種:
- 一種是引數欄位就是同一個結構體中的平級欄位,
- 另一種是引數欄位為結構中其他欄位的欄位,
約束語法很簡單,如果是約束同一個結構中的欄位,則在基礎的 Tags 后面添加一個 field 后綴,例如:eqfield 定義欄位間的相等(eq)約束,如果是更深層次的欄位,在 field 之前還需要加上 cs(Cross-Struct),eq 就變為了 eqcsfield,
示例:
type RegisterForm struct {
Name string `validate:"min=2"`
Age int `validate:"min=18"`
Password string `validate:"min=10"`
Password2 string `validate:"eqfield=Password"`
}
即:他們組成就是 “比較符號 + 是否跨 Struct(cross struct) + field”:
- eqfield=Field:必須等于 Field 的值,
- nefield=Field:必須不等于 Field 的值,
- gtfield=Field:必須大于 Field 的值,
- gtefield=Field: 必須大于等于 Field 的值,
- ltfield=Field:必須小于 Field 的值,
- ltefield=Field:必須小于等于 Field 的值,
- eqcsfield=Other.Field:必須等于 struct Other 中 Field 的值,
- necsfield=Other.Field:必須不等于 struct Other 中 Field 的值,
- gtcsfield=Other.Field:必須大于 struct Other 中 Field 的值;
- gtecsfield=Other.Field:必須大于等于 struct Other 中 Field 的值,
- ltcsfield=Other.Field:必須小于 struct Other 中 Field 的值,
- ltecsfield=Other.Field:必須小于等于 struct Other 中 Field 的值,
另外還有幾個挺有用的 Tag:
- required_with=Field1 Field2:在 Field1 或者 Field2 存在時,必須;
- required_with_all=Field1 Field2:在 Field1 與 Field2 都存在時,必須;
- required_without=Field1 Field2:在 Field1 或者 Field2 不存在時,必須;
- required_without_all=Field1 Field2:在 Field1 與 Field2 都存在時,必須;
自定義約束
除了使用 validator 提供的內建約束外,還可以定義自己的約束,首先定義一個型別為 func (validator.FieldLevel) bool 的函式檢查約束是否滿足,可以通過 FieldLevel 取出要檢查的欄位的資訊,然后,呼叫校驗器的 RegisterValidation() 方法將該約束注冊到指定的名字上,最后我們就可以在結構體中使用該約束了,
示例:
type RegisterForm struct {
Name string `validate:"palindrome"`
Age int `validate:"min=18"`
}
func reverseString(s string) string {
runes := []rune(s)
for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 {
runes[from], runes[to] = runes[to], runes[from]
}
return string(runes)
}
func CheckPalindrome(fl validator.FieldLevel) bool {
value := fl.Field().String()
return value == reverseString(value)
}
func main() {
validate := validator.New()
validate.RegisterValidation("palindrome", CheckPalindrome)
f1 := RegisterForm{
Name: "djd",
Age: 18,
}
err := validate.Struct(f1)
if err != nil {
fmt.Println(err)
}
f2 := RegisterForm{
Name: "dj",
Age: 18,
}
err = validate.Struct(f2)
if err != nil {
fmt.Println(err)
}
}
錯誤處理
validator 回傳的錯誤有兩種,一種是引數錯誤,一種是校驗錯誤,它們都實作了 error 介面,
- 引數錯誤時,回傳 InvalidValidationError 型別;
- 校驗錯誤時,回傳 ValidationErrors 型別,ValidationErrors 是一個錯誤切片,保存了每個欄位違反的每個約束資訊,
所以 validator 校驗回傳的結果只有 3 種情況:
- nil:沒有錯誤;
- InvalidValidationError:輸入引數錯誤;
- ValidationErrors:欄位違反約束,
我們可以在程式中判斷 err != nil 時,可以依次將 err 轉換為 InvalidValidationError 和 ValidationErrors 以獲取更詳細的資訊:
func processErr(err error) {
if err == nil {
return
}
invalid, ok := err.(*validator.InvalidValidationError)
if ok {
fmt.Println("param error:", invalid)
return
}
validationErrs := err.(validator.ValidationErrors)
for _, validationErr := range validationErrs {
fmt.Println(validationErr)
}
}
func main() {
validate := validator.New()
err := validate.Struct(1)
processErr(err)
err = validate.VarWithValue(1, 2, "eqfield")
processErr(err)
}
中文錯誤資訊
需要安裝兩個包:
go get github.com/go-playground/universal-translator
go get github.com/go-playground/locales
示例:
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
type Users struct {
Name string `form:"name" json:"name" validate:"required"`
Age uint8 `form:"age" json:"age" validate:"required,gt=18"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Name: "admin",
Age: 12,
Passwd: "123",
Code: "123456",
}
// 中文翻譯器
uni := ut.New(zh.New())
trans, _ := uni.GetTranslator("zh")
// 校驗器
validate := validator.New()
// 注冊翻譯器到校驗器
err := zh_translations.RegisterDefaultTranslations(validate, trans)
if err!=nil {
fmt.Println(err)
}
err = validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Translate(trans))
return
}
}
return
}
參考檔案
https://blog.csdn.net/qq_26273559/article/details/107164846
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/139252.html
標籤:其他
上一篇:執行./byfn.sh up報錯ERROR! Fabric Docker image version of 1.4.1 does not match this newer version of BYF
下一篇:區塊鏈交易所軟體開發技術
