Gin框架自帶參數(shù)校驗的使用詳解
gin 框架內(nèi)置參數(shù)驗證,寫在 binging tag 中,如下所示:
type Tag struct {
ID int32 `json:"id" binding:"required"`
Name string `json:"name" binding:"required"`
}這個驗證器是由 validator 提供的,文檔
常用的操作符:
,:且,多個驗證之間同時滿足|:或,滿足其中一個-:跳過驗證=:等于
例子來源于官方文檔:
type User struct {
FirstName string `binding:"required"`
LastName string `binding:"required"`
Age uint8 `binding:"gte=0,lte=130"`
Email string `binding:"required,email"` // 驗證是否是一個有效的 email 地址
Gender string `binding:"oneof=male female prefer_not_to` // oneof 表示只能是其中之一 可以用 eq=male|eq=female|eq=prefer_not_to 代替
FavouriteColor string `binding:"iscolor"` // iscolor 表示是否是一個有效的顏色值
Addresses []*Address `binding:"required,dive,required"` // dive 對嵌套結(jié)構體進行遞歸驗證
}
type Address struct {
Street string `binding:"required"`
City string `binding:"required"`
Planet string `binding:"required"`
Phone string `binding:"required"`
}在我們?nèi)粘V袝?jīng)常做手機號驗證,但是官方只有一個 e164 的驗證,e164 是國際通用標準(要加區(qū)號),就不太符合國內(nèi)用戶
這就需要自定義驗證器 mobile,根據(jù)官網(wǎng)提供的例子,實現(xiàn)它也是比較簡單的
自定義驗證器
首先定義一個 ValidateMobile 方法
import (
"regexp"
"github.com/go-playground/validator/v10"
)
func ValidateMobile(fl validator.FieldLevel) bool {
mobile := fl.Field().String()
ok, _ := regexp.MatchString(`^1([38][0-9]|14[579]|5[^4]16[6]|7[1-35-8]|9[189])\d{8}$`, mobile) // 用正則去匹配
return ok
}將方法注冊到 validator 中
import (
"github.com/gin-gonic/gin/binding"
)
// 從 gin 中獲取到 validator 驗證器,然后注冊 mobile
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
}自定義驗證器就定義好了,使用的時候就可以這樣寫了
type User struct {
Name string `json:"name" binding:"required"`
Mobile string `json:"mobile" binding:"required,mobile"`
}錯誤信息翻譯
validator 默認錯誤信息是英文,如果需要翻譯成中文,自己做轉(zhuǎn)換:
- 跳過
json tag為-的字段 - 實例化
zh和en翻譯包 - 初始化翻譯器
- 注冊翻譯器
- 調(diào)用
InitTrans,傳入zh或者en即可
具體代碼如下:
import (
"fmt"
"mxshop_api/order_web/global"
"reflect"
"strings"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
"github.com/go-playground/validator/v10"
ut "github.com/go-playground/universal-translator"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
func InitTrans(local string) (err error) {
// 修改 gin 中 validator 實現(xiàn)定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// 將 json tag 作為字段名
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// 如果 json tag 為 - 則不處理
if name == "-" {
return ""
}
return name
})
zhT := zh.New()
enT := en.New()
// 第一個參數(shù)是備用的語言環(huán)境,后面的參數(shù)是應該支持的語言環(huán)境
uni := ut.New(enT, zhT, enT)
// 初始化翻譯器
global.Trans, ok = uni.GetTranslator(local)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", local)
}
switch local {
case "en":
en_translations.RegisterDefaultTranslations(v, global.Trans)
case "zh":
zh_translations.RegisterDefaultTranslations(v, global.Trans)
default:
en_translations.RegisterDefaultTranslations(v, global.Trans)
}
return
}
return
}使用:
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
}這個翻譯器只能夠翻譯內(nèi)置的錯誤信息,自定義的驗證器需要自己翻譯
比如我們上面定義的 mobile 驗證器,如果驗證失敗,返回的錯誤信息需要我們自己翻譯
參照官方例子,如下代碼:
// 四個參數(shù)
// 需要翻譯的字段名 翻譯器實例 翻譯方法 返回錯誤的信息
_ = validator.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
// 翻譯的內(nèi)容
return ut.Add("mobile", "{0} 非法的手機號碼!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
// fe.Field() 獲取到的是字段名,而不是 json tag
t, _ := ut.T("mobile", fe.Field())
return t
})這樣就可以翻譯自定義的驗證器了,最終代碼如下:
// 注冊驗證器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
_ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
return ut.Add("mobile", "{0} 非法的手機號碼!", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("mobile", fe.Field())
return t
})
}格式化返回信息
validator 默認返回的錯誤信息是 json tag,這不是我們希望的
我們希望拋出去的錯誤是結(jié)構化的
- 使用
shouldBindJSON將參數(shù)接寫到對應的結(jié)構體中 - 如果解析失敗,則斷言這個錯誤為
validator.ValidationErrors - 如果斷言成功,則將錯誤進行翻譯
- 去掉結(jié)構體中的前綴,并生成一個
map - 將這個
map拋出去
最終代碼如下:
if err := ctx.ShouldBindJSON(&bannerForm); err != nil {
HandleValidatorError(ctx, err)
return
}
func HandleValidatorError(c *gin.Context, err error) {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
}
c.JSON(http.StatusBadRequest, gin.H{
"error": removeTopStruct(errs.Translate(global.Trans)),
})
return
}
func removeTopStruct(fields map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fields {
// 去掉結(jié)構體中的前綴 {User.mobile: "mobile 非法的手機號碼!"}
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}錯誤類型在文檔中有說明type FieldError
Tag():得到的是你寫的require、mobile等Field():結(jié)構體中定義的字段名Translate():翻譯
總結(jié)
validator 能夠滿足我們?nèi)粘5男枨螅?/p>
- 對嵌套結(jié)構體進行遞歸驗證
- 自定義驗證器
- 錯誤信息翻譯
- 格式化返回信息
到此這篇關于Gin框架自帶參數(shù)校驗的使用詳解的文章就介紹到這了,更多相關Gin參數(shù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

