验证规则: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 ", 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 //}