Gin框架中參數(shù)校驗優(yōu)化詳解
原始方式
gin使用的是 github.com/go-playground/validator 該組件進行入?yún)⑿r?,如下是gin中常用的參數(shù)校驗方式:
type AccountCreateForm struct { Id uint64 `json:"id"` Name string `json:"name" binding:"required,max=16"` // 使用required和max限制入?yún)楸靥铐椗c最大長度不能超過16字符 Username string `json:"username" binding:"required"` Password string `json:"password"` }
該種方式有如下幾個不好使的地方:
錯誤提示不友好,如果不做任何處理,默認參數(shù)校驗不通過會返回如下錯誤提示
不支持正則表達式
不支持自定義錯誤描述
改進
自定義validatorx(validator擴展工具包)
注冊翻譯器,新增對校驗錯誤進行轉(zhuǎn)譯方法
package validatorx import ( "mayfly-go/pkg/utils/stringx" "mayfly-go/pkg/utils/structx" "reflect" "strings" "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" zh_trans "github.com/go-playground/validator/v10/translations/zh" ) const CustomMsgTagName = "msg" var ( trans ut.Translator ) func Init() { // 獲取gin的校驗器 validate, ok := binding.Validator.Engine().(*validator.Validate) if !ok { return } // 修改返回字段key的格式 validate.RegisterTagNameFunc(func(fld reflect.StructField) string { // 如果存在校驗錯誤提示消息,則使用字段名,后續(xù)需要通過該字段名獲取相應錯誤消息 if _, ok := fld.Tag.Lookup(CustomMsgTagName); ok { return fld.Name } name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] if name == "-" { return "" } return name }) // 注冊翻譯器 zh := zh.New() uni := ut.New(zh, zh) trans, _ = uni.GetTranslator("zh") // 注冊翻譯器 zh_trans.RegisterDefaultTranslations(validate, trans) // 注冊自定義正則表達式校驗器 validate.RegisterValidation(CustomPatternTagName, patternValidFunc) // 注冊自定義正則校驗規(guī)則 RegisterCustomPatterns() } // Translate 翻譯錯誤信息 func Translate(data any, err error) map[string][]string { var result = make(map[string][]string) errors := err.(validator.ValidationErrors) for _, err := range errors { fieldName := err.Field() // 判斷該字段是否設置了自定義的錯誤描述信息,存在則使用自定義錯誤信息進行提示 if field, ok := structx.IndirectType(reflect.TypeOf(data)).FieldByName(fieldName); ok { if errMsg, ok := field.Tag.Lookup(CustomMsgTagName); ok { customMsg := getCustomErrMsg(err.Tag(), errMsg) if customMsg != "" { result[fieldName] = append(result[fieldName], customMsg) continue } } } // 如果是自定義正則校驗規(guī)則,則使用自定義的錯誤描述信息 if err.Tag() == CustomPatternTagName { result[fieldName] = append(result[fieldName], fieldName+patternErrMsg[err.Param()]) continue } result[fieldName] = append(result[fieldName], err.Translate(trans)) } return result } // 獲取自定義的錯誤提示消息 // // @param validTag 校驗標簽,如required等 // @param customMsg 自定義錯誤消息 func getCustomErrMsg(validTag, customMsg string) string { // 解析 msg:"required=用戶名不能為空,min=用戶名長度不能小于8位" msgs := strings.Split(customMsg, ",") for _, msg := range msgs { tagAndMsg := strings.Split(stringx.Trim(msg), "=") if len(tagAndMsg) > 1 && validTag == stringx.Trim(tagAndMsg[0]) { // 獲取valid tag對應的錯誤消息 return stringx.Trim(tagAndMsg[1]) } } return customMsg } // Translate 翻譯錯誤信息為字符串 func Translate2Str(data any, err error) string { res := Translate(data, err) errMsgs := make([]string, 0) for _, v := range res { errMsgs = append(errMsgs, v...) } return strings.Join(errMsgs, ", ") }
自定義正則表達式校驗方式
package validatorx import ( "mayfly-go/pkg/global" "regexp" "github.com/go-playground/validator/v10" ) const CustomPatternTagName = "pattern" var ( regexpMap map[string]*regexp.Regexp // key:正則表達式名稱 value:正則表達式 patternErrMsg map[string]string // key:正則表達式名稱 value:校驗不通過時的錯誤消息提示 ) // 注冊自定義正則表達式校驗規(guī)則 func RegisterCustomPatterns() { // 賬號用戶名校驗,使用該種方式可以復用正則表達式以及錯誤提示 // 使用方式如:Username string `json:"username" binding:"pattern=account_username"` RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允許輸入5-20位大小寫字母、數(shù)字、下劃線") } // 注冊自定義正則表達式 func RegisterPattern(patternName string, regexpStr string, errMsg string) { if regexpMap == nil { regexpMap = make(map[string]*regexp.Regexp, 0) patternErrMsg = make(map[string]string) } regexpMap[patternName] = regexp.MustCompile(regexpStr) patternErrMsg[patternName] = errMsg } // 自定義正則表達式校驗器函數(shù) func patternValidFunc(f validator.FieldLevel) bool { reg := regexpMap[f.Param()] if reg == nil { global.Log.Warnf("%s的正則校驗規(guī)則不存在!", f.Param()) return false } return reg.MatchString(f.Field().String()) }
錯誤轉(zhuǎn)譯
對入?yún)⑦M行校驗,檢驗不通過時將錯誤進行轉(zhuǎn)譯,轉(zhuǎn)譯為漢字或自定義的錯誤描述等。
// 綁定并校驗請求結(jié)構(gòu)體參數(shù) func BindJsonAndValid[T any](g *gin.Context, data T) T { if err := g.ShouldBindJSON(data); err != nil { // 統(tǒng)一recover處理 panic(ConvBindValidationError(data, err)) } else { return data } } // 綁定請求體中的json至form結(jié)構(gòu)體,并拷貝至另一結(jié)構(gòu)體 func BindJsonAndCopyTo[T any](g *gin.Context, form any, toStruct T) T { BindJsonAndValid(g, form) structx.Copy(toStruct, form) return toStruct } // 轉(zhuǎn)譯參數(shù)校驗錯誤,并將參數(shù)校驗錯誤為業(yè)務異常錯誤(統(tǒng)一recover處理) func ConvBindValidationError(data any, err error) error { if e, ok := err.(validator.ValidationErrors); ok { // 調(diào)用validatorx.Translate2Str方法進行校驗錯誤轉(zhuǎn)譯 return biz.NewBizErrCode(403, validatorx.Translate2Str(data, e)) } return err } // 返回失敗結(jié)果集 func ErrorRes(g *gin.Context, err any) { switch t := err.(type) { case biz.BizError: g.JSON(http.StatusOK, model.Error(t)) case error: g.JSON(http.StatusOK, model.ServerError()) global.Log.Errorf("%s\n%s", t.Error(), string(debug.Stack())) case string: g.JSON(http.StatusOK, model.ServerError()) global.Log.Errorf("%s\n%s", t, string(debug.Stack())) default: global.Log.Error(t) } }
初始化校驗器
項目啟動時,在合適的時機初始化校驗器
// 參數(shù)校驗器初始化、如錯誤提示中文轉(zhuǎn)譯、注冊自定義校驗器等 validatorx.Init()
統(tǒng)一使用方式
入?yún)⒆侄蝨ag綁定
type AccountCreateForm struct { Id uint64 `json:"id"` // msg tag里對應的required max即為binding里的校驗類型 Name string `json:"name" binding:"required,max=16" msg:"required=姓名不能為空,max=姓名最大長度不能超過16位"` // account_name為validatorx.RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允許輸入5-20位大小寫字母、數(shù)字、下劃線") Username string `json:"username" binding:"pattern=account_username"` Password string `json:"password" binding:"required"` }
form := &form.AccountCreateForm{} // 校驗不通過會自行panic統(tǒng)一recover處理 var account *entity.Account = ginx.BindJsonAndCopyTo(rc.GinCtx, form, new(entity.Account))
效果
更多代碼詳見:gitee.com/objs/mayfly-go一個web版 linux(終端[終端回放] 文件 腳本 進程 計劃任務)、數(shù)據(jù)庫(mysql postgres)、redis(單機 哨兵 集群)、mongo統(tǒng)一管理操作平臺
到此這篇關于Gin框架中參數(shù)校驗優(yōu)化詳解的文章就介紹到這了,更多相關Gin參數(shù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang中文字符串截取函數(shù)實現(xiàn)原理
在golang中可以通過切片截取一個數(shù)組或字符串,但是當截取的字符串是中文時,可能會出現(xiàn)問題,下面我們來自定義個函數(shù)解決Golang中文字符串截取問題2018-03-03Golang基于泛化調(diào)用與Nacos實現(xiàn)Dubbo代理
這篇文章主要為大家詳細介紹了Golang如何基于泛化調(diào)用與Nacos實現(xiàn)Dubbo代理,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-04-04Go語言之使用pprof工具查找goroutine(協(xié)程)泄漏
這篇文章主要介紹了Go語言之使用pprof工具查找goroutine(協(xié)程)泄漏,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01Go語言RPC Authorization進行簡單ip安全驗證的方法
這篇文章主要介紹了Go語言RPC Authorization進行簡單ip安全驗證的方法,實例分析了Go語言進行ip驗證的技巧,需要的朋友可以參考下2015-03-03