原創 frank Golang語言開發棧
2024年11月10日 23:00 北京 聽全文
大家好,我是 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 的值是否是 JSON 或 XML,若是,則使用 json 或 xml 標籤,若不是,則再使用 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(®ister); 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(®ister, binding.JSON); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"data": login,
"data2": register,
})
})
r.Run()
}
需要注意的是,該方式會影響效能,所以儘量避免需要多次繫結的使用場景。
還有就是隻有 JSON、XML、MsgPack、ProtoBuf 使用 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,讀者朋友們可以閱讀其官方文件,瞭解更多使用方式。
推薦閱讀
Go 語言併發程式設計之互斥鎖 sync.Mutex
Go 語言併發程式設計互斥鎖 sync.Mutex 底層實現
Go 語言泛型使用詳解
Go 語言中怎麼使用依賴注入?
Wire:Go語言依賴注入的利器
參考資料
[1]
Validator 文件: https://pkg.go.dev/github.com/go-playground/validator/v10
閱讀 1535