Gin基于go-playground/validator实现自定义IsValid接口校验逻辑
Gin 通用自定义IsValid校验器实现
核心实现逻辑
你只需要通过validator.FieldLevel提供的方法拿到当前字段的反射值,处理指针/值类型的边界情况后做接口断言即可。注意代码需要引入reflect标准库完成反射操作,补全后的校验注册代码如下:
import ( "reflect" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" ) // 全局定义通用IsValid接口 type IsValid interface { IsValid() bool } // 注册自定义校验逻辑 func init() { if v, ok := binding.Validator.Engine().(*validator.Validate); ok { // validator默认会对标签名做小写归一化,这里注册名统一用全小写避免匹配歧义 v.RegisterValidation("isvalid", func(fl validator.FieldLevel) bool { field := fl.Field() // 处理指针类型字段:如果是nil指针直接判定为校验不通过 if field.Kind() == reflect.Ptr { if field.IsNil() { return false } field = field.Elem() } // 先尝试直接断言:覆盖值类型实现接口、指针类型被Elem转成值类型的场景 isValidInstance, ok := field.Interface().(IsValid) if ok { return isValidInstance.IsValid() } // 兼容值类型字段、IsValid方法为指针接收者的场景:如果字段可寻址,取地址后再断言 if field.CanAddr() { isValidInstance, ok = field.Addr().Interface().(IsValid) if ok { return isValidInstance.IsValid() } } // 字段未实现IsValid接口,校验不通过 return false }) } }
使用注意事项
- 结构体标签要和注册的校验名匹配,直接写
binding:"isValid"或者binding:"isvalid"都可以,validator内部会自动转小写匹配,使用示例:// 示例:自定义手机号类型实现IsValid接口 type Phone string func (p Phone) IsValid() bool { // 替换为实际业务校验逻辑,比如正则匹配手机号格式 return len(p) == 11 } // 请求结构体 type UserReq struct { Name string `json:"name" binding:"required"` Phone Phone `json:"phone" binding:"required,isvalid"` // 加isvalid标签即可触发自定义校验 } - 如果你的自定义类型用指针接收者实现
IsValid()方法,结构体字段既可以声明为指针类型(比如Phone *Phone),也可以声明为值类型,上面的兼容逻辑已经处理了可寻址场景的断言问题,不需要额外修改。 - 指针字段如果为
nil,默认会判定为校验不通过,如果你有业务需求允许空指针跳过校验,可以把指针判空分支的return false改成return true即可。
内容的提问来源于stack exchange,提问作者Manuelarte
相关产品推荐
相关产品推荐

