Gin 框架怎麼驗證綁定到結構體的字段?

大家好,我是 frank。「Golang 語言開發棧」公衆號作者。

01 介紹

在使用 Gin 框架開發項目時,通常我們選擇模型綁定的方式接收請求參數,我們在上一遍文章中,已經介紹過使用 Gin 框架接收請求參數的常用方式。

本文我們主要介紹怎麼驗證綁定到結構體的字段,順便補充關於模型綁定的一些內容。

02 模型綁定

關於 Gin 框架的模型綁定,我們在上一篇文章中介紹了 ShouldBind 方法,該方式也是我們在使用 Gin 框架開發項目時,最常使用的方式。

一般使用場景

示例代碼:

package main

import "github.com/gin-gonic/gin"

func main() {
 r := gin.Default()
 r.GET("/login", func(c *gin.Context) {
  var login Login
  if err := c.ShouldBind(&login); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  c.JSON(200, gin.H{
   "data": login,
  })
 })
 r.Run()
}

type Login struct {
 User     string `form:"user"`
 Password string `form:"password"`
}

輸出結果:

curl -s -X GET http://127.0.0.1:8080/login\?user\=frank\&password\=123456 | jq
{
  "data"{
    "User""frank",
    "Password""123456"
  }
}

閱讀上面這段代碼,我們使用 GET 請求方式,需要給結構體中的字段,添加 tag form

需要注意的是,當我們使用 ShouldBind 方式時,如果使用 GET 請求方式,Gin 框架只會使用 form 標籤;

如果使用 POST 請求方式,Gin 框架首先檢查 content-type 的值是否是 JSONXML,若是,則使用 jsonxml 標籤,若不是,則再使用 form 標籤。

特殊使用場景

示例代碼:

package main

import "github.com/gin-gonic/gin"

func main() {
 r := gin.Default()
 r.POST("/login", func(c *gin.Context) {
  var login Login
  var register Register
  if err := c.ShouldBind(&login); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  if err := c.ShouldBind(&register); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  c.JSON(200, gin.H{
   "data":  login,
   "data2": register,
  })
 })
 r.Run()
}

type Login struct {
 User     string `form:"user" json:"user"`
 Password string `form:"password" json:"password"`
}

type Register struct {
 User     string `form:"user" json:"user"`
 Password string `form:"password" json:"password"`
}

輸出結果:

curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"frank", "password": "123456"}' | jq
{
  "error""EOF"
}

閱讀上面這段代碼,將同一次請求,綁定到多個結構體,我們使用 ShouldBind 方式,得到的輸出結果是 EOF,這是因爲 ShouldBind 使用了 Request.Body,它不可以重用。

當使用一次 ShouldBind 之後,Request.Body 的值是 EOF,再次使用 ShoudBind 就會返回錯誤。

我們可以使用 ShoudBindBodyWith 解決該問題,ShouldBindBodyWith 在綁定之前會將 body 存儲到上下文中。

我們只需要修改上面這段代碼,即可實現多次綁定,示例代碼:

func main() {
 r := gin.Default()
 r.POST("/login", func(c *gin.Context) {
  var login Login
  var register Register
  if err := c.ShouldBindBodyWith(&login, binding.JSON); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  if err := c.ShouldBindBodyWith(&register, binding.JSON); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  c.JSON(200, gin.H{
   "data":  login,
   "data2": register,
  })
 })
 r.Run()
}

需要注意的是,該方式會影響性能,所以儘量避免需要多次綁定的使用場景。

還有就是隻有 JSONXMLMsgPackProtoBuf 使用 ShouldBind 多次綁定,會出現該問題。其它格式,可以使用 ShouldBind 多次綁定,並且不會影響性能。

03 驗證

接下來,我們介紹 Gin 框架綁定到結構體的字段的驗證方式。

Gin 框架提供了 2 種綁定方式,一種是我們已經介紹的 ShouldBind*,該方式是 ShouldBindWith* 的快捷方式。ShouldBind*ShouldBindWith* 方式可以返回錯誤。

另一種是 Bind*,該方式是 MustBindWith* 的快捷方式。該方式不可以返回錯誤,也就是如果發生綁定錯誤,則請求終止。我們一般很少使用該方式。

我們使用 ShouldBind* 方式爲例,介紹怎麼驗證綁定到結構體的字段。

標籤驗證(字段級驗證)

示例代碼:

package main

import (
 "github.com/gin-gonic/gin"
)

func main() {
 r := gin.Default()
 r.POST("/login", func(c *gin.Context) {
  var login Login
  if err := c.ShouldBind(&login); err != nil {
   c.JSON(200, gin.H{
    "error": err.Error(),
   })
   return
  }
  c.JSON(200, gin.H{
   "data": login,
  })
 })
 r.Run()
}

type Login struct {
 User     string `form:"user" json:"user" binding:"required"`
 Password string `form:"password" json:"password"`
}

輸出結果:

curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"", "password": "123456"}' | jq
{
  "error""Key: 'Login.User' Error:Field validation for 'User' failed on the 'required' tag"
}

curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"frank", "password": "123456"}' | jq
{
  "error""Key: 'Login.User' Error:Field validation for 'User' failed on the 'len' tag"
}

閱讀上面這段代碼,我們在結構體 Login 的字段 User 標籤中,新增 binding:"required,len=10",請求參數中,故意在請求時將 user 的值設置爲空字符串和長度不等於 10 的字符串,返回結果給出了驗證錯誤的信息。

實際上,Gin 框架使用 github.com/go-playground/validator/v10 進行驗證。

除了 required 和 len 之外,還有很多屬性,讀者朋友們可以閱讀 Validator 文檔 [1]。

04 總結

本文我們介紹 Gin 框架怎麼驗證綁定到結構體的字段,分爲字段級驗證(標籤驗證)和結構體級驗證,限於篇幅,本文我們先只介紹字段級驗證。

Gin 框架中的驗證,使用的是三方庫 validator,讀者朋友們可以閱讀其官方文檔,瞭解更多使用方式。

參考資料

[1] Validator 文檔: https://pkg.go.dev/github.com/go-playground/validator/v10

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ILamNLmJ6BfnHOJuTv86sQ