驗證規則:https://github.com/go-playground/validator
中文參考:https://www.cnblogs.com/zj420255586/p/13542395.html
https://www.cnblogs.com/wangkun122/articles/11023964.html
驗證器構建

package validate_1 import ( "errors" "github.com/go-playground/locales/en" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "gopkg.in/go-playground/validator.v9" entranslations "gopkg.in/go-playground/validator.v9/translations/en" zhtranslations "gopkg.in/go-playground/validator.v9/translations/zh" "reflect" "unicode/utf8" ) /* https://www.jianshu.com/p/9ef19d5eac72 構建驗證器 numeric:數字 required :必填 email_1:驗證字符串是email格式;例:"email_1" url:這將驗證字符串值包含有效的網址;例:"url" max:字符串最大長度;例:"max=20" min:字符串最小長度;例:"min=6" excludesall:不能包含特殊字符;例:"excludesall=0x2C"//注意這里用十六進制表示。 len:字符長度必須等於n,或者數組、切片、map的len值為n,即包含的項目數;例:"len=6" eq:數字等於n,或者或者數組、切片、map的len值為n,即包含的項目數;例:"eq=6" ne:數字不等於n,或者或者數組、切片、map的len值不等於為n,即包含的項目數不為n,其和eq相反;例:"ne=6" gt:數字大於n,或者或者數組、切片、map的len值大於n,即包含的項目數大於n;例:"gt=6" gte:數字大於或等於n,或者或者數組、切片、map的len值大於或等於n,即包含的項目數大於或等於n;例:"gte=6" lt:數字小於n,或者或者數組、切片、map的len值小於n,即包含的項目數小於n;例:"lt=6" lte:數字小於或等於n,或者或者數組、切片、map的len值小於或等於n,即包含的項目數小於或等於n;例:"lte=6" 跨字段驗證 eqfield=Field: 必須等於 Field 的值; nefield=Field: 必須不等於 Field 的值; gtfield=Field: 必須大於 Field 的值; gtefield=Field: 必須大於等於 Field 的值; ltfield=Field: 必須小於 Field 的值; ltefield=Field: 必須小於等於 Field 的值; eqcsfield=Other.Field: 必須等於 struct Other 中 Field 的值; necsfield=Other.Field: 必須不等於 struct Other 中 Field 的值; gtcsfield=Other.Field: 必須大於 struct Other 中 Field 的值; gtecsfield=Other.Field: 必須大於等於 struct Other 中 Field 的值; ltcsfield=Other.Field: 必須小於 struct Other 中 Field 的值; ltecsfield=Other.Field: 必須小於等於 struct Other 中 Field 的值; */ type UserInfo struct { Name string `json:"name" validate:"checkName" label:"姓名" checkName_err:"姓名長度不能大於5"` //聲明自定義tag,函數必須定義,必須綁定 FirstName string `json:"first_name" validate:"required" label:"姓"` LastName string `json:"last_name" validate:"required" label:"名"` Age uint8 `json:"age" validate:"gte=0,lte=100" label:"年齡"` Email string `json:"email_1" validate:"required,email" label:"郵箱"` } // 自定義驗證函數 func checkName(fl validator.FieldLevel) bool { count := utf8.RuneCountInString(fl.Field().String()) if count > 5 { return false } return true } //func main() { // user := UserInfo{ // Name: "rosers", // FirstName: "Badger", // LastName: "Smith", // Age: 105, // Email: "", // } // c := make(map[string]validator.Func, 0) // c["checkName"] = checkName // res := autoValidate(user, "zh", c) // //res := autoValidate(user, map[string]validator.Func{}, "zh") // fmt.Println("res=", res) //} //通用驗證器 func AutoValidate(s interface{}, language string, customValidate map[string]validator.Func) []string { errs := make([]string, 0) //驗證器 validate := validator.New() //注冊字段翻譯 if language != "en" { validate.RegisterTagNameFunc(func(fld reflect.StructField) string { name := fld.Tag.Get("label") return name }) } if customValidate != nil { //注冊自定義規則 for k, v := range customValidate { err := validate.RegisterValidation(k, v) if err != nil { panic(err) } } } //語言切換 var trans ut.Translator var err1 error switch language { case "en": trans, _ = ut.New(en.New()).GetTranslator(language) err1 = entranslations.RegisterDefaultTranslations(validate, trans) case "zh": trans, _ = ut.New(zh.New()).GetTranslator(language) err1 = zhtranslations.RegisterDefaultTranslations(validate, trans) default: err1 = errors.New("語言不存在") } if err1 != nil { panic(err1) } if customValidate != nil { //獲取reflect.Type 類型 typ := reflect.TypeOf(s) //獲取reflect.Value 類型 val := reflect.ValueOf(s) //獲取到a對應的類別 kd := val.Kind() //如果傳入的不是struct,就退出 if kd != reflect.Struct { panic("expect struct") } //獲取到該結構體有幾個字段 num := val.NumField() //注冊自定義規則 for k, _ := range customValidate { //變量結構體的所有字段 for i := 0; i < num; i++ { //獲取到struct標簽, 注意需要通過reflect.Type來獲取tag標簽的值 tagVal := typ.Field(i).Tag.Get(k + "_err") //如果該字段於tag標簽就顯示,否則就不顯示 if tagVal != "" { //注冊自定義函數返回信息 validate.RegisterTranslation(k, trans, func(ut ut.Translator) error { return ut.Add(k, tagVal, true) }, func(ut ut.Translator, fe validator.FieldError) string { t, _ := ut.T(k, fe.Field(), fe.Field()) return t }) } } } } err := validate.Struct(s) if err != nil { for _, err := range err.(validator.ValidationErrors) { errs = append(errs, err.Translate(trans)) } } return errs }
gin構建驗證

package ginvalidate import ( "fmt" "github.com/gin-gonic/gin" "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_translations "github.com/go-playground/validator/v10/translations/zh" "reflect" ) func init() { AutoValidate(nil) } var trans ut.Translator type AutoRule struct { Func validator.Func `json:"func"` FuncName string `json:"func_name"` ErrMsg string `json:"err_msg"` } func AutoValidate(customValidate []AutoRule) { uni := ut.New(zh.New()) trans, _ = uni.GetTranslator("zh") if v, ok := binding.Validator.Engine().(*validator.Validate); ok { //注冊翻譯器 _ = zh_translations.RegisterDefaultTranslations(v, trans) //注冊自定義函數 for _, autoRule := range customValidate { _ = v.RegisterValidation(autoRule.FuncName, autoRule.Func) //根據提供的標記注冊翻譯 v.RegisterTranslation(autoRule.FuncName, trans, func(ut ut.Translator) error { return ut.Add(autoRule.FuncName, autoRule.ErrMsg, true) }, func(ut ut.Translator, fe validator.FieldError) string { t, _ := ut.T(autoRule.FuncName) return t }) } //注冊一個函數,獲取struct tag里自定義的json作為字段名 v.RegisterTagNameFunc(func(fld reflect.StructField) string { name := fld.Tag.Get("json") return name }) } } //翻譯輸出錯誤 func ValidateErrs(err error) map[string]string { //對驗證結果進行翻譯 dumpErrs := make(map[string]string, 0) errs := err.(validator.ValidationErrors) for _, e := range errs { dumpErrs[e.Field()] = e.Translate(trans) } return dumpErrs } func ValidateBind(c *gin.Context, obj interface{}) map[string]string { fmt.Printf("T=%T,v=%v\n", obj, obj) //對驗證結果進行翻譯 err := c.ShouldBindJSON(obj) errs1 := make(map[string]string, 0) if err == nil { return errs1 } //翻譯 errs := err.(validator.ValidationErrors) for _, e := range errs { errs1[e.Field()] = e.Translate(trans) } return errs1 } //單條錯誤 func ValidateErr(err error) string { if err == nil { return "" } errs := err.(validator.ValidationErrors) for _, e := range errs { return e.Translate(trans) } return "" }
自定義使用

package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "go_code/pack/gin_validate/ginvalidate" "net/http" "time" ) // 綁定為json 驗證 //binding有與validate相似的驗證效果 type Login struct { User string `json:"user" binding:"required,max=10,min=5"label:"用戶名"` Password string `json:"password" binding:"required"label:"密碼"` CheckIn time.Time `json:"check_in" binding:"required" label:"輸入時間"` } func main() { router := gin.Default() // Example for binding JSON ({"user": "manu", "password": "123"}) //注冊路由 router.POST("/loginJSON", login) // Listen and serve on 0.0.0.0:8080 router.Run(":8088") } func login(c *gin.Context) { fmt.Println(123) //自定義 //c1 := make(map[string]validator.Func, 0) //c1["密碼長度不正確"] = passrule //s1 := []ginvalidate.AutoRule{ginvalidate.AutoRule{ // Func: passrule, // FuncName: "passrule", // ErrMsg: "密碼長度不能小於5", //}} //ginvalidate.AutoValidate(s1) var json Login err := c.ShouldBindJSON(&json) errs := ginvalidate.ValidateErrs(err) //errs := ginvalidate.ValidateBind(c, json) fmt.Println(errs) if len(errs) != 0 { c.JSON(http.StatusBadRequest, errs) return } c.JSON(http.StatusOK, json) return } func passrule(fl validator.FieldLevel) bool { str := fl.Field().String() if len(str) < 5 { return false } return true } //func bookableDate(fl validator.FieldLevel) bool { // date, ok := fl.Field().Interface().(time.Time) // // if ok { // today := time.Now() // if today.After(date) { // return false // } // } // return true //}