Versions Compared
compared with
Key
- This line was added.
- This line was removed.
- Formatting was changed.
自定义规则注册
虽然gvalid
已经内置了常见的数十种校验规则,但是在部分业务场景下我们需要自定义校验规则,特别是一些可以重复使用的业务相关的校验规则。当然,gvalid
如此的强大,她已经为您考虑得如此周全。校验规则注册:如此的强大,她已经为您考虑得如此周全。自定义规则分为两种:全局规则注册和局部规则注册。
全局规则注册
全局规则是全局生效的规则,注册之后无论是使用方法还是对象来执行数据校验都可以使用自定义的规则。
注册校验方法:
Code Block | ||
---|---|---|
| ||
// RegisterRule registers custom validation rule and function for package.
// It returns error if there's already the same rule registered previously.
func RegisterRule(rule string, f RuleFunc) error |
校验方法定义:
Code Block | ||
---|---|---|
| ||
// RuleFunc is the custom function for data validation.
// The parameter |
`rule` specifies the validation rule string, like "required", "between:1,100", etc. // The parameter |
`value` specifies the value for this rule to validate. // The parameter |
`message` specifies the custom error message or configured i18n message for this rule. // The parameter |
`data` specifies |
the `data` which is passed to the |
Validator. It might be type of map/struct or a nil value. // You can ignore the parameter |
`data` if |
you do not really need it in your custom validation rule.
type RuleFunc func(ctx context.Context, rule string, value interface{}, message string, |
data |
interface{}) error |
简要说明:
- 您需要按照
RuleFunc
类型的方法定义,实现一个您需要的校验方法,随后使用RegisterRule
注册到gvalid
模块中全局管理。该注册逻辑往往是在程序初始化时执行。该方法在对数据进行校验时将会被自动调用,方法返回nil
表示校验通过,否则应当返回一个非空的error
类型值。 - 参数说明:
ctx
表示当前链路的上下文变量。rule
参数表示当前的校验规则,包含规则的参数,例如:required
,between:1,100
,length:6
等等。value
参数表示被校验的数据值,注意类型是一个interface{}
,因此您可以传递任意类型的参数,并在校验方法内部按照程序使用约定自行进行转换(往往使用gconv
模块实现转换)。message
参数表示在校验失败后返回的校验错误提示信息,往往在struct
定义时使用gvalid tag
来通过标签定义。params
参数表示校验时传递的所有参数,例如校验的是一个data
参数表示校验时传递的参数,例如校验的是一个map
或者struct
时,往往在联合校验时有用。时,往往在联合校验时有用。需要注意的是,这个值是运行时输入的,值可能是nil
。
Tip |
---|
自定义错误默认情况下已支持 |
Note |
---|
注意事项:自定义规则的注册方法不支持并发调用,您需要在程序启动时进行注册(例如在 |
示例1,用户唯一性规则
在用户注册时,我们往往需要校验当前用户提交的名称/账号是否唯一,因此我们可以注册一个unique-name
的全局规则来实现。
Code Block | ||
---|---|---|
| ||
package main import ( "context" "fmt" "github.com/gogf/gf/errorsutil/gerrorgvalid" "github.com/gogf/gf/frameerrors/ggerror" "github.com/gogf/gf/utilframe/gconvg" "github.com/gogf/gf/util/gvalidgconv" ) // 用户唯一性校验规则: unique-name type User struct { Id int Name string `v:"required|unique-name # 请输入用户名称|用户名称已被占用"` Pass string `v:"required|length:6,18"` } func init() { rule := "unique-name" if err := gvalid.RegisterRule(rule, UniqueNameChecker); err != nil { panic(err) } } func UniqueNameChecker(ctx context.Context, rule string, value interface{}, message string, paramsdata map[string]interface{}) error { var (user := &User{} idif v, ok := gconvdata.Int(params["Id"])(*User); ok { nameuser = gconv.String(value)v )} // SELECT COUNT(*) FROM `user` WHERE `id` != xxx AND `name` != xxx count, err := g.Model("user"). Ctx(ctx). WhereNot("id", iduser.Id). WhereNot("name", namegconv.String(value)). Count() if err != nil { return err } if count > 0 { return gerror.New(message) } return nil } func main() { // 注册自定义校验规则 rule := "unique-name" if err := gvalid.RegisterRule(rule, UniqueNameChecker); err != nil { panic(err) } type User struct { Id int Name string `v:"required|unique-name # 请输入用户名称|用户名称已被占用"` Pass string `v:"required|length:6,18"` } user := &User{ Id: 1, Name: "john", Pass: "123456", } err := g.Validator().CheckStruct(user) fmt.Println(err.Error()) } |
示例2,自定义覆盖required
规则
默认情况下,gvalid
的内置规则是不支持map
、slice
类型的required
规则校验,因此我们可以自行实现,然后覆盖原有规则(这里需要用到反射)。其他的规则也可以按照此逻辑自定义覆盖。
Code Block | ||
---|---|---|
| ||
package main import ( "context" "errors" "fmt" "github.com/gogf/gf/util/gvalid" "math" "reflect" "github.com/gogf/gf/frame/g" ) func init() { "github.com/gogf/gf/util/gvalid" ) // 自定义的必需规则校验rule := "required" if err := gvalid.RegisterRule(rule, RequiredChecker); err != nil { panic(err) } } func RequiredChecker(ctx context.Context, rule string, value interface{}, message string, paramsdata map[string]interface{}) error { reflectValue := reflect.ValueOf(value) if reflectValue.Kind() == reflect.Ptr { reflectValue = reflectValue.Elem() } isEmpty := false switch reflectValue.Kind() { case reflect.Bool: isEmpty = !reflectValue.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: isEmpty = reflectValue.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: isEmpty = reflectValue.Uint() == 0 case reflect.Float32, reflect.Float64: isEmpty = math.Float64bits(reflectValue.Float()) == 0 case reflect.Complex64, reflect.Complex128: c := reflectValue.Complex() isEmpty = math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 case reflect.String, reflect.Map, reflect.Array, reflect.Slice: isEmpty = reflectValue.Len() == 0 } if isEmpty { return errors.New(message) } return nil } func main() { // 注册自定义校验规则,覆盖原有的required校验规则 rule := "required" if err := gvalid.RegisterRule(rule, RequiredChecker); err != nil { panic(err) } fmt.Println(g.Validator().Rules("required").Messages("It's required").CheckValue("")) fmt.Println(g.Validator().Rules("required").Messages("It's required").CheckValue([]string{})) fmt.Println(g.Validator().Rules("required").Messages("It's required").CheckValue(map[string]int{})) } |
执行后,终端输出:
Code Block | ||
---|---|---|
| ||
It's required It's required It's required |
Panel | ||
---|---|---|
| ||
|