Gin請求參數校驗

gin參數校驗使用的是 validator庫,因此本文的內容同樣適用於使用validator來做參數校驗的地方

校驗規則

完整的校驗規則可參考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各個字段定義了校驗內容,但是我在MyStructTimeout字段使用了structonly標記,那麼Timeout中定義的校驗內容將不再生效

  • nostructlevel

    nostructlevel 類似於structonly,唯一不同之處是要想忽略規則的效果生效,必須要使用requiredomitempty

    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) // 校驗數據
    }
    

    注:MustBindWithShouldBindWith的區別是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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章