校驗規則
完整的校驗規則可參考https://godoc.org/github.com/go-playground/validator,下面分享常用的校驗規則規則:
-
數字值限制
注:如果限制之間存在衝突,如
eq=10,ne=10
,則會根據先後順序,後面的會覆蓋前面的定義,以後面定義的爲準;校驗內容爲eq=10,ne=10
,只會生效ne=10
,如果存在衝突,所有校驗規則均符合此規律。- 限制範圍
max=10 # 最大值爲10,即小於等於10 min=10 # 最小值爲10,即大於等於10 gt=10 # 大於 10 gte=10 # 大於等於10 lt=10 # 小於10 lte=10 # 小於等於10
- 限制值
eq=10 # 等於10 ne=10 # 不等於10 oneof=1 2 3 # 值只能是1、2 或 3 len=3 # 限制位數爲3位數
- 限制範圍
-
字符串長度限制
-
限制長度範圍
max=10 # 最大長度爲10 min=10 # 最小長度爲10 gt=10 # 長度大於10 lt=10 # 長度小於10 gte=10 # 長度大於等於10 let=10 # 長度小於等於10
-
限制值內容
eq=aaa # 值爲aaa ne=aaa # 值不能爲aaa oneof=a b c # 枚舉,只能爲a、b 或 c len=3 # 字符長度爲3
-
-
存在性校驗
注:這裏需要特別注意的是,這裏是否存在是根據0值與非0值判斷的,如果字段中傳了對應的0值,也會被認爲是不存在的
-
必選
required # 必須
-
可選
omitempty,xxx=xxx # 可選,如果存在,則繼續向後校驗規則xxx=xxx,如果不存在,則xxx=xxx不生效,但是如果omitempty之前存在校驗規則,則前面的校驗規則還是生效的,如 gte=-1,omitempty,len=3,則gte=-1規則始終生效,而len=3只有在值不爲0時生效。
-
關聯校驗
required_with=AAA # 當AAA存在時,此字段也必須存在 required_with_all=AAA BBB CCC # 當AAA BBB CCC 都存在時,此字段必須存在 required_without=AAA # 當AAA不存在時,此字段必須存在 required_without_all=AAA BBB # 當AAA BBB 都不存在時,此字段必須存在
-
-
結構體字段間校驗
-
一個層級內部校驗
eqfield=AAA # 和字段AAA值相等 nefield=AAA # 和字段AAA值不相等 gtfield=AAA # 大於字段AAA的值 gtefield=AAA # 大於等於字段AAA的值 ltfield=AAA # 小於字段AAA的值 ltefield=AAA # 小於等於AAA字段的值
-
多個層級之間校驗
eqcsfield=AAA.B # 等於AAA中的B字段 necsfield=AAA.B # 不等於AAA中的B字段 gtcsfield=AAA.B # 大於AAA中的B字段 gtecsfield=AAA.B # 大於等於AAA中的B字段 ltcsfield=AAA.B # 小於AAA中的B字段 ltecsfield=AAA.B # 小於等於AAA中的B字段
注:此處要注意的是,多個結構體之間的校驗必須是字段之間存在層級聯繫,且只能是上層的與下層的做關聯,不能反過來,如下:
type Timeout struct { Connect int `json:"connect" validate:"required"` Read int `json:"read" validate:"required"` Send int `json:"send" validate:"required"` } type MyStruct struct { Name string `json:"name" validate:"required"` Out int `json:"out" validate:"eqcsfield=Timeout.Connect"` // 只能是Timeout同層級的去關聯Timeout下的字段,不能Timeout下的字段關聯上層字段 Timeout Timeout `json:"timeout"` }
-
-
dive 對數組校驗
dive 的意思是潛水,潛水也就是深入到水下,dive在這裏的意思是也差不多,就是更深入一層的意思,如當前字段類型是
[]string
,那麼深入一層,就從整體(array)
到局部(array中的一個元素)
,對應到校驗上,就是dive前是對整體校驗,dive後是對整體中的元素校驗。示例:-
一般數組
Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
檢驗內容:
[]string
長度必須大於0,數組中元素string長度必須在1-100之間 -
結構體數組校驗
Cors []AStruct `binding:"dive"`
雖然這裏
dive
前後未定義相關校驗規則,但是如果要啓用 AStruct 中定義的校驗規則,那麼dive是必須的,否則AStruct
中定義的規則不會生效
-
-
dive對Map校驗
dive
的含義在這裏仍然類似於數組,整體 -> 局部
的規則不變,但是map的特殊性在於不僅有value值,還有key值,這裏就需要方法去區分是校驗value,還是校驗key;這裏使用了 keys 和 endkeys來標記key值的校驗範圍,從keys開始,至endkeys結束ReqHeaders map[string]string `binding:"dive,keys,min=1,max=100,endkeys,required,min=1,max=100"`
校驗內容:未對整體做校驗,限制key值長度必須在1-100之間,value值的長度也必須在1-100之間
-
structonly
當一個結構體定義了校驗規則,但是在某些地方,不需要這些校驗規則生效的時候,可以使用
structonly
標記,存在此標記的結構體內部的校驗規則將不會再生效。如下:type Timeout struct { Connect int `json:"connect" binding:"required"` Read int `json:"read" binding:"required"` Send int `json:"send" binding:"required"` } type MyStruct struct { Name string `json:"name" binding:"required"` Timeout Timeout `json:"timeout" binding:"structonly"` }
結構體
Timeout
各個字段定義了校驗內容,但是我在MyStruct
的Timeout
字段使用了structonly
標記,那麼Timeout
中定義的校驗內容將不再生效 -
nostructlevel
nostructlevel 類似於structonly,唯一不同之處是要想忽略規則的效果生效,必須要使用
required
或omitempty
type Timeout struct { Connect int `json:"connect" binding:"required,gte=1,lte=1000"` Read int `json:"read" binding:"gte=1,lte=1000"` Send int `json:"send" binding:"gte=1,lte=1000"` } type MyStruct struct { Name string `form:"name" json:"name" binding:"required"` UseMeta bool `json:"use_meta"` Timeout Timeout `json:"timeout" binding:"required,nostructlevel"` }
-
忽略校驗規則
- # 忽略字段校驗規則,常用於嵌套結構中,用來忽略嵌套結構下的校驗規則
-
多校驗規則
或
運算| # 多個檢驗規則默認之間是與關係,使用 | 可實現多運算規則之間進行或運算
gin中的使用方法
1. 校驗參數定義
校驗參數是定義在結構體上的,因此在首先需要定義好接受數據的結構體,再定義每個屬性的校驗參數:
type Timeout struct {
Connect int `binding:"omitempty,gte=1,lte=75"`
Read int `binding:"omitempty,gte=1,lte=1000"`
Send int `binding:"omitempty,gte=1,lte=1000"`
}
type MyTestStruct struct {
Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
Name string `binding:"required"`
UseTimeout bool
Timeout Timeout `binding:"required_with=UseTimeout"`
}
結構體字段 binding
tag的內容就是校驗參數。
2. 數據綁定
gin中提供了常用的 ShouldBindWith/ShouldBindUri
方法,來實現自動的數據與結構體的綁定,提供了以下類型的數據:
數據類型 | Tag | Context-Type | 數據源 | 使用示例 |
---|---|---|---|---|
JSON | json | application/json | Body | ShouldBindWith(&data, binding.Query) |
XML | xml | application/xml、text/xml | Body | ShouldBindWith(&data, binding.XML) |
Form | form | application/x-www-form-urlencoded | Body | c.ShouldBindWith(&data, binding.Form) |
Query | form | url query | ShouldBindWith(&data, binding.Query) | |
FormPost | form | application/x-www-form-urlencoded | Body | ShouldBindWith(&data, binding.FormPost) |
FormMultipart | form | multipart/form-data | Body | ShouldBindWith(&data, binding.FormMultipart) |
ProtoBuf | application/x-protobuf | Body | ShouldBindWith(&data, binding.ProtoBuf) | |
MsgPack | application/x-msgpack、application/msgpack | Body | ShouldBindWith(&data, binding.MsgPack) | |
YAML | application/x-yaml | Body | ShouldBindWith(&data, binding.YAML) | |
Uri | uri | uri | ShouldBindUri(&data) | |
Header | header | Header | ShouldBindWith(&forwardRule, binding.Header) |
說明:
-
數據類型:指的是傳輸的數據類型
-
Tag:結構體定義的Tag,揭示了數據字段與結構體字段的對應關係,如:Form類型數據中
id
對應結構體中的ID
字段,那麼就需要定義一下內容:type xxxx struct { ... ID int64 `form:"id"` ... }
-
Context-Type:HTTP中的Header 字段之一,表示Body的數據類型,
gin.Context
中的Bind
方法會根據Content-Type
自動綁定數據到對應類型上func (c *Context) Bind(obj interface{}) error { b := binding.Default(c.Request.Method, c.ContentType()) // 獲取數據類型 return c.MustBindWith(obj, b) // 校驗數據 }
注:
MustBindWith
與ShouldBindWith
的區別是MustBindWith
會自動返回錯誤信息,如果你有統一的錯誤返回格式,MustBindWith
可能會破壞返回格式的統一性。 -
數據源:指的是從哪裏取數據,
Body
指的是從HTTP Body中獲取數據,url query
指從URL的quey參數中獲取數據,url
指的是從url中獲取參數,如:/api/v1/xxx/:id
,id參數就是從url中來獲取的 -
使用示例:使用的例程
3. 校驗結果獲取
ShouldBindWith
會返回error信息,如果error信息不爲空,則表明出現錯誤,錯誤信息就包含在error中,如果error爲空,則表明校驗通過,示例如下:
var data MyTestStruct // 存儲數據內容
// 獲取 & 校驗數據
if err = c.ShouldBindWith(&data, binding.JSON); err != nil {
// 出現錯誤,打印錯誤內容
fmt.Println(err)
return
}
// 校驗成功,打印數據內容
fmt.Println(data)