- InvalidValidationError">type InvalidValidationError
- ValidationErrors">type ValidationErrors
- FieldError">type FieldError
- FieldLevel">type FieldLevel
- StructLevel">type StructLevel
- Func">type Func
- FuncCtx">type FuncCtx
- StructLevelFunc">type StructLevelFunc
- StructLevelFuncCtx">type StructLevelFuncCtx
- Validate">type Validate
- 用法
- 例一:基本使用
- 例二:自定义字段(验证字段)
- 例三:自定义字段(验证结构体一)
- 例四:自定义字段(验证结构体二)
- 例五:输出中文错误提示
- 例六:自定义错误
- 例七:利用反射自定义错误信息
- 例八:嵌套验证错误自定义
项目地址:https://github.com/go-playground/validator
gin使用的验证器
type InvalidValidationError
无效验证错误,该错误很少去判断
type InvalidValidationError struct {Type reflect.Type}
type ValidationErrors
验证错误信息数组
type ValidationErrors []FieldError
type FieldError
type FieldError interface {// 返回验证失败的标签// eg. alias "iscolor",will return "iscolor"Tag() string// 返回验证失败的真实标签// eg. alias "iscolor",will return "hexcolor|rgb|rgba|hsl|hsla"ActualTag() string// 返回验证失败的 结构体.字段// 当使用validate.Field(...)进行验证是,会返回空Namespace() string// 返回验证失败的 结构体.字段// 当使用validate.Field(...)进行验证是,会返回空StructNamespace() string// 返回验证失败的字段Field() string// 返回验证失败的字段StructField() string// 返回验证失败的值Value() interface{}// 给一个正确的值作为参考Param() string// Kind returns the Field's reflect KindKind() reflect.Kind// Type returns the Field's reflect TypeType() reflect.Type}
type FieldLevel
type FieldLevel interface {// 返回第一层结构体Top() reflect.Value// 返回当前字段的父结构体Parent() reflect.Value// 返回当前字段的值Field() reflect.Value// 返回当前字段名FieldName() string// 返回当前字段名(真实)StructFieldName() string// 返回当前字段设定的值Param() string}
type StructLevel
type StructLevel interface {//返回主验证对象Validator() *Validate// 返回第一层结构体Top() reflect.Value// 返回当前结字段的父结构体Parent() reflect.Value// 返回当前结构体Current() reflect.Value// 通过传递字段和标记信息来报告错误ReportError(field interface{}, fieldName, structFieldName string, tag, param string)}
type Func
Func接受所有验证需求的现场级接口。验证成功时,返回值应该为true。
type Func func(fl FieldLevel) bool
type FuncCtx
type FuncCtx func(ctx context.Context, fl FieldLevel) bool
type StructLevelFunc
type StructLevelFunc func(sl StructLevel)
type StructLevelFuncCtx
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
type Validate
func New() Validate 返回一个“validate”的新实例
func (v Validate) SetTagName(name string) 更改“validate”的默认标记名
func (v Validate) Struct(s interface{}) error 结构体验证,并验证被嵌套的结构体
func (v Validate) StructCtx(ctx context.Context, s interface{}) (err error)
func (v Validate) Var(field interface{}, tag string) error 验证单个变量
func (v Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull …bool) error
- 使用给定的标签添加验证
func (v Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull …bool) error
func (v Validate) RegisterStructValidation(fn StructLevelFunc, types …interface{})
- 为结构体添加验证
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types …interface{})
用法
- :跳过验证,例 `validate:"-"`,常出现在嵌套验证中| :或操作,例 `validate:"rbg|rgba"`,接受rbg或rgba类型的值dive :潜入切片,数组或映射,如果是多维嵌套,那么可以用多个dive,dive...structonly:不对嵌套结构体中的任何字段进行验证omitempty :如果是空值,忽略其他的验证,注意放的位置,放在最前面,表示对后面的所有要求生效required :必须字段oneof :枚举,`validate:"oneof=1 2"`required_with_all :当默些字段存在时才验证该字段 例required_with_all = f1 f2required_without : 当默些字段不存在时才验证该字段 例required_without = f1 f2len : 对于数字型是==判断,对于其他是length判断 例 len = 5max : 数字验证最大,其他验证length 例 max = 5min : 数字验证最小,其他验证length 例 min = 5eq : 数字与字符串验证==,其他验证length 例 eq = 5ne : 与eq相反,!=gt : 大于gte : 大于等于lt : 小于lte : 小于等于eqfield : 判断该字段是否等于另外一个字段 eqfield=ConfirmPassword: 或validate.VarWithValue(password, confirmpassword, "eqfield")nefield : 与eqfield相反unique : 对于数组和片,unique将确保没有重复。对于映射,unique将确保没有重复的值numeric : 判断字符类型是否只包含整数或浮点数hexadecimal :验证字符串值是否包含有效的十六进制hexcolor : 将验证字符串值是否包含有效的十六进制颜色,包括hashtag (#)rgb : 验证字符串值是否包含有效的rgb颜色email : 验证字符串值是否包含有效的电子邮件json : 验证字符串值是否为json格式file : 验证字符串值是否包含有效的文件路径url : 将验证字符串值是否包含有效的url,必须包含一个模式,例如http://或rtmp://uri : 验证字符串值是否包含有效的uribase64 : 验证字符串值是否包含有效的base64值contains : 验证字符串是否包含莫子串 例 contains = acontainsany : 验证字符串是否包含莫子串的任意一个 例 containsany = abcexcludes : 与contains相反,是不包含excludesall : 与containsany相反,是不包含任意一个startswith : 验证字符串是否已某个字符串开头endswith : 结尾uuid : 判断字符串是否是uuid类型uuid3uuid4uuid5latitude : 是否包含有效的纬度longitude : 是否包含有效的经度ipipv4ipv6datetime : 字符串值是否包含有效的日期tcp_addrtcp4_addrtcp6_addr......
例一:基本使用
package mainimport ("fmt""github.com/go-playground/validator/v10")// User contains user informationtype User struct {FirstName string `validate:"required"`LastName string `validate:"required"`Age uint8 `validate:"gte=0,lte=130"`Email string `validate:"required,email"`FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...}// Address houses a users address informationtype Address struct {Street string `validate:"required"`City string `validate:"required"`Planet string `validate:"required"`Phone string `validate:"required"`}// use a single instance of Validate, it caches struct infovar validate *validator.Validatefunc main() {validate = validator.New()validateStruct()validateVariable()}func validateStruct() {address := &Address{Street: "Eavesdown Docks",City : "Beijing",Planet: "Persphone",Phone: "none",}user := &User{FirstName: "Badger",LastName: "Smith",Age: 135,Email: "Badger.Smith@gmail.com",FavouriteColor: "#000",Addresses: []*Address{address},}// returns nil or ValidationErrors ( []FieldError )err := validate.Struct(user)if err != nil {for _, err := range err.(validator.ValidationErrors) {fmt.Println(err.Namespace()) //User.Agefmt.Println(err.Field()) //Agefmt.Println(err.StructNamespace()) //User.Agefmt.Println(err.StructField()) //Agefmt.Println(err.Tag()) //ltefmt.Println(err.ActualTag()) //ltefmt.Println(err.Kind()) //uint8fmt.Println(err.Type()) //uint8fmt.Println(err.Value()) //135fmt.Println(err.Param()) //130}}}func validateVariable() {myEmail := "joeybloggs.gmail.com"errs := validate.Var(myEmail, "required,email")if errs != nil {fmt.Println(errs) // Key: "" Error:Field validation for "" failed on the "email" tagreturn}}
例二:自定义字段(验证字段)
package mainimport ("fmt""github.com/go-playground/validator/v10""time")type Address struct {Street string `validate:"required"`City string `validate:"required"`Planet string `validate:"required"`Phone string `validate:"required"`Age int `validate:"min=12,max=15"`CreateAt time.Time `validate:"required,myParam"` // 日期必须大于2000-01-01}func main() {address := &Address{Street: "Eavesdown Docks",City: "beijing",Planet: "Persphone",Phone: "none",Age: 12,CreateAt : time.Now(),}validate := validator.New()//自己定义tag标签以及与之对应的处理逻辑validate.RegisterValidation("myParam", mytimeFunc)//查看是否符合验证err := validate.Struct(address)fmt.Println(err)}func mytimeFunc(fl validator.FieldLevel) bool {format:= "2006-01-02"param,_ :=time.ParseInLocation(format,"2000-01-01",time.Local)param1,ok:=fl.Field().Interface().(time.Time)if !ok || param1.Before(param){return false}return true}
例三:自定义字段(验证结构体一)
package mainimport ("fmt""github.com/go-playground/validator/v10""time")type Time struct {CreateTime time.Time `validate:"required,myParam"`T int}type Address struct {Street string `validate:"required"`City string `validate:"required"`Planet string `validate:"required"`Phone string `validate:"required"`Age int `validate:"min=12,max=15"`CreateAt *Time `validate:"required"` // 日期必须大于2000-01-01}func main() {CreateAt := &Time{CreateTime:time.Now(),}address := &Address{Street: "Eavesdown Docks",City: "beijing",Planet: "Persphone",Phone: "none",Age: 12,CreateAt : CreateAt,}validate := validator.New()//自己定义tag标签以及与之对应的处理逻辑validate.RegisterValidation("myParam", mytimeFunc)//查看是否符合验证err := validate.Struct(address)fmt.Println(err)}func mytimeFunc(fl validator.FieldLevel) bool {format:= "2006-01-02"param,_ :=time.ParseInLocation(format,"2000-01-01",time.Local)param1,ok:=fl.Field().Interface().(time.Time)fmt.Println(fl.FieldName())if !ok || param1.Before(param){return false}return true}
例四:自定义字段(验证结构体二)
package mainimport ("fmt""github.com/go-playground/validator/v10""time")type Time struct {CreateTime time.Time `validate:"required"`T int `validate:"required"`}type Address struct {Street string `validate:"required"`City string `validate:"required"`Planet string `validate:"required"`Phone string `validate:"required"`Age int `validate:"min=12,max=15"`CreateAt *Time `validate:"required"` // 日期必须大于2000-01-01,T必须<=10}func main() {CreateAt := &Time{CreateTime:time.Now(),T:11,}address := &Address{Street: "Eavesdown Docks",City: "beijing",Planet: "Persphone",Phone: "none",Age: 12,CreateAt : CreateAt,}validate := validator.New()//自己定义tag标签以及与之对应的处理逻辑validate.RegisterStructValidation(mytimeFunc, &Time{})//查看是否符合验证err := validate.Struct(address)fmt.Println(err)}func mytimeFunc(sl validator.StructLevel){format:= "2006-01-02"param,_ :=time.ParseInLocation(format,"2000-01-01",time.Local)t := sl.Current().Interface().(Time)if t.T > 10{sl.ReportError(t.T,"T","T","max","10")}if t.CreateTime.Before(param){sl.ReportError(t.T,"CreateTime","CreateTime","after","2000-01-01")}}
例五:输出中文错误提示
import “github.com/go-playground/validator/translations/zh”
该包只有一个对外函数
func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error)
ps:在与v10结合时出了问题,拉不下来
.\demo2.go:28:43: cannot use validate (type *"github.com/go-playground/validator/v10".Validate)as type *"gopkg.in/go-playground/validator.v9".Validate in argument to"github.com/go-playground/validator/translations/zh".RegisterDefaultTranslations
拉下来后需要改一下源码import的内容,按下面源码改即可
或:因为只有一个文件,把代码copy下来作为一个包(推荐)
https://github.com/go-playground/validator/blob/master/translations/zh/zh.go
package mainimport ("fmt""github.com/go-playground/locales/zh""github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zh_translater "study/validate/zh")type UserInfo struct {FirstName string `validate:"required"`LastName string `validate:"required"`Age uint8 `validate:"gte=0,lte=100"`Email string `validate:"required,email"`}func main() {//中文翻译器zh_ch := zh.New()uni := ut.New(zh_ch)trans, _ := uni.GetTranslator("zh")//验证器validate := validator.New()//验证器注册翻译器zh_translater.RegisterDefaultTranslations(validate, trans)user := &UserInfo{FirstName: "Badger",LastName: "Smith",Age: 105,Email: "",}err := validate.Struct(user)if err != nil {for _, err := range err.(validator.ValidationErrors) {//翻译错误信息fmt.Println(err.Translate(trans))}return}fmt.Println("success")}//输出:Age必须小于或等于100Email为必填字段
例六:自定义错误
package mainimport ("fmt""github.com/go-playground/validator/v10")type UserInfo struct {Age uint8 `validate:"required,gte=0,lte=100"`Email string `validate:"required,email"`}func (self *UserInfo) GetErrors(errs validator.ValidationErrors) string{errstr := ""for _,err :=range errs{if err.Field() == "Age"{if err.Tag() == "required"{errstr += "年龄必填,"}else if err.Tag() == "gte" || err.Tag() == "lte"{errstr += "年龄需在0-100之间,"}}if err.Field() == "Email"{if err.Tag() == "required"{errstr += "邮箱必填,"}else if err.Tag() == "email"{errstr += "邮箱格式错误,"}}}return errstr}func main() {validate := validator.New()user := &UserInfo{Age: 105,Email: "",}err := validate.Struct(user)if err != nil {errstr := user.GetErrors(err.(validator.ValidationErrors))//断言fmt.Println(errstr) //年龄需在0-100之间,邮箱必填,return}fmt.Println("success")}
例七:利用反射自定义错误信息
package mainimport ("fmt""github.com/go-playground/validator/v10""reflect")var validate *validator.Validatetype UserInfo struct {Age uint8 `validate:"required,gte=0,lte=100" message:"年龄需在0-100之间"`Email string `validate:"required,email" message:"email字段必须为结构体"`}func StructToValidate(structInter interface{}) (map[string]string, bool) {structType := reflect.TypeOf(structInter)var resultMap map[string]string = map[string]string{}validErr := validate.Struct(structInter)if validErr != nil {for _, err := range validErr.(validator.ValidationErrors) {field, isOK := structType.FieldByName(err.Field())if isOK {// fmt.Println("validmsg------------------------>",field.Tag.Get("validmsg"))resultMap[err.Field()] = field.Tag.Get("message")}}return resultMap, false} else {return nil, true}}func main() {validate = validator.New()user := UserInfo{Age: 105,Email: "",}results, isOK :=StructToValidate(user)if !isOK{fmt.Println(results) //map[Age:年龄需在0-100之间 Email:email 字段必须为结构体]}}
例八:嵌套验证错误自定义
基于例子七:对结构体嵌结构体有用,对结构体嵌数组等暂时不行
package mainimport ("fmt""reflect")type AAAA struct {Name string `name22:"bbb"`Age string `name22:"ccc"`}type AAA struct {Aaaa []AAAA `name11:"aaa"`}func main() {var a AAAat := reflect.TypeOf(a)field,ok := at.FieldByName("Aaaa")if ok {if field.Type.Kind() == reflect.Slice{tag := field.Type.Elem().Field(0).Tag.Get("name22")fmt.Println(tag) // bbbfmt.Println("------------------------")field = field.Type.Elem().Field(0)fmt.Println(field) // {Name string name22:"bbb" 0 [0] false}}}}
func StructToValidate(structInter interface{}) (map[string]string, bool) {structType := reflect.TypeOf(structInter)var resultMap map[string]string = map[string]string{}validErr := validate.Struct(structInter)if validErr != nil {for _, err := range validErr.(validator.ValidationErrors) {var field reflect.StructFieldvar isOK boolif strings.Count(err.StructNamespace(),".") == 1{field, isOK = structType.FieldByName(err.Field())}else {// 命名空间包含的.大于1,说明是嵌套验证structT := structTypefor i:=1;i<strings.Count(err.StructNamespace(),".");i++{fieldName := strings.Split(err.StructNamespace(),".")[i]field,isOK = structT.FieldByName(fieldName)if isOK{structT = field.Type}}field, isOK = structT.FieldByName(err.Field())}if isOK {resultMap[err.Field()] = field.Tag.Get("validmsg")}else {resultMap[err.Field()] = err.Field()+"不能为空"}}return resultMap, false}return nil, true}
