相关数据结构

自定义规则方法定义,以及对应的输入参数数据结构。

  1. // RuleFuncInput holds the input parameters that passed to custom rule function RuleFunc.
  2. type RuleFuncInput struct {
  3. // Rule specifies the validation rule string, like "required", "between:1,100", etc.
  4. Rule string
  5. // Message specifies the custom error message or configured i18n message for this rule.
  6. Message string
  7. // Value specifies the value for this rule to validate.
  8. Value *gvar.Var
  9. // Data specifies the `data` which is passed to the Validator. It might be a type of map/struct or a nil value.
  10. // You can ignore the parameter `Data` if you do not really need it in your custom validation rule.
  11. Data *gvar.Var
  12. }
  13. // RuleFunc is the custom function for data validation.
  14. type RuleFunc func(ctx context.Context, in RuleFuncInput) error

方法参数简要说明:

  1. 上下文参数ctx是必须的。
  2. RuleFuncInput数据结构说明:
    • Rule表示当前的校验规则,包含规则的参数,例如:required, between:1,100, length:6等等。
    • Message参数表示在校验失败后返回的校验错误提示信息。
    • Value参数表示被校验的数据值,注意类型是一个*gvar.Var泛型,因此您可以传递任意类型的参数。
    • Data参数表示校验时传递的参数,例如校验的是一个map或者struct时,往往在联合校验时有用。需要注意的是,这个值是运行时输入的,值可能是nil

自定义错误默认情况下已支持i18n特性,因此您只需要按照 gf.gvalid.rule.自定义规则名称 配置i18n转译信息即可,该信息在校验失败时会自动从i18n管理器获取后,通过Message参数传入给您注册的自定义校验方法中。

全局校验规则注册

自定义规则分为两种:全局规则注册和局部规则注册。

全局规则是全局生效的规则,注册之后无论是使用方法还是对象来执行数据校验都可以使用自定义的规则。

注册校验方法:

  1. // RegisterRule registers custom validation rule and function for package.
  2. func RegisterRule(rule string, f RuleFunc) {
  3. customRuleFuncMap[rule] = f
  4. }
  5. // RegisterRuleByMap registers custom validation rules using map for package.
  6. func RegisterRuleByMap(m map[string]RuleFunc) {
  7. for k, v := range m {
  8. customRuleFuncMap[k] = v
  9. }
  10. }

您需要按照RuleFunc类型的方法定义,实现一个您需要的校验方法,随后使用RegisterRule注册到gvalid模块中全局管理。该注册逻辑往往是在程序初始化时执行。该方法在对数据进行校验时将会被自动调用,方法返回nil表示校验通过,否则应当返回一个非空的error类型值。

注意事项:自定义规则的注册方法不支持并发调用,您需要在程序启动时进行注册(例如在boot包中处理),无法在运行时动态注册,否则会产生并发安全问题。

示例1,订单ID存在校验

在电商业务中,当我们对订单进行操作时,可以通过自定义规则校验给定的订单ID是否存在,因此我们可以注册一个order-exist的全局规则来实现。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/v2/database/gdb"
  6. "github.com/gogf/gf/v2/errors/gerror"
  7. "github.com/gogf/gf/v2/frame/g"
  8. "github.com/gogf/gf/v2/os/gctx"
  9. "github.com/gogf/gf/v2/util/gvalid"
  10. "time"
  11. )
  12. type Request struct {
  13. OrderId int64 `v:"required|order-exist"`
  14. ProductName string
  15. Amount int64
  16. // ...
  17. }
  18. func init() {
  19. rule := "order-exist"
  20. gvalid.RegisterRule(rule, RuleOrderExist)
  21. }
  22. func RuleOrderExist(ctx context.Context, in gvalid.RuleFuncInput) error {
  23. // SELECT COUNT(*) FROM `order` WHERE `id` = xxx
  24. count, err := g.Model("order").
  25. Ctx(ctx).
  26. Cache(gdb.CacheOption{
  27. Duration: time.Hour,
  28. Name: "",
  29. Force: false,
  30. }).
  31. WhereNot("id", in.Value.Int64()).
  32. Count()
  33. if err != nil {
  34. return err
  35. }
  36. if count == 0 {
  37. return gerror.Newf(`invalid order id "%d"`, in.Value.Int64())
  38. }
  39. return nil
  40. }
  41. func main() {
  42. var (
  43. ctx = gctx.New()
  44. req = &Request{
  45. OrderId: 65535,
  46. ProductName: "HikingShoe",
  47. Amount: 10000,
  48. }
  49. )
  50. err := g.Validator().CheckStruct(ctx, req)
  51. fmt.Println(err)
  52. }

示例2,用户唯一性规则

在用户注册时,我们往往需要校验当前用户提交的名称/账号是否唯一,因此我们可以注册一个unique-name的全局规则来实现。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/v2/database/gdb"
  6. "github.com/gogf/gf/v2/errors/gerror"
  7. "github.com/gogf/gf/v2/frame/g"
  8. "github.com/gogf/gf/v2/os/gctx"
  9. "github.com/gogf/gf/v2/util/gvalid"
  10. "time"
  11. )
  12. type User struct {
  13. Id int
  14. Name string `v:"required|unique-name#请输入用户名称|用户名称已被占用"`
  15. Pass string `v:"required|length:6,18"`
  16. }
  17. func init() {
  18. rule := "unique-name"
  19. gvalid.RegisterRule(rule, RuleUniqueName)
  20. }
  21. func RuleUniqueName(ctx context.Context, in gvalid.RuleFuncInput) error {
  22. var user *User
  23. if err := in.Data.Scan(&user); err != nil {
  24. return gerror.Wrap(err, `Scan data to user failed`)
  25. }
  26. // SELECT COUNT(*) FROM `user` WHERE `id` != xxx AND `name` != xxx
  27. count, err := g.Model("user").
  28. Ctx(ctx).
  29. Cache(gdb.CacheOption{
  30. Duration: time.Hour,
  31. Name: "",
  32. Force: false,
  33. }).
  34. WhereNot("id", user.Id).
  35. WhereNot("name", user.Name).
  36. Count()
  37. if err != nil {
  38. return err
  39. }
  40. if count > 0 {
  41. if in.Message != "" {
  42. return gerror.New(in.Message)
  43. }
  44. return gerror.Newf(`user name "%s" is already token by others`, user.Name)
  45. }
  46. return nil
  47. }
  48. func main() {
  49. var (
  50. ctx = gctx.New()
  51. user = &User{
  52. Id: 1,
  53. Name: "john",
  54. Pass: "123456",
  55. }
  56. )
  57. err := g.Validator().CheckStruct(ctx, user)
  58. fmt.Println(err)
  59. }

局部校验规则注册

局部规则是仅在当前校验对象下生效规则,校验规则是注册到当前使用的链式操作流程中而不是全局中。

注册方法:

  1. // RuleFunc registers one custom rule function to current Validator.
  2. func (v *Validator) RuleFunc(rule string, f RuleFunc) *Validator
  3. // RuleFuncMap registers multiple custom rule functions to current Validator.
  4. func (v *Validator) RuleFuncMap(m map[string]RuleFunc) *Validator

简要介绍:

  • RuleFunc方法用于注册单个自定义校验规则到当前对象。
  • RuleFuncMap方法用于注册多个自定义校验规则到当前对象。

使用示例:

我们将上面其中一个例子改为局部校验规则注册。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/v2/database/gdb"
  6. "github.com/gogf/gf/v2/errors/gerror"
  7. "github.com/gogf/gf/v2/frame/g"
  8. "github.com/gogf/gf/v2/os/gctx"
  9. "github.com/gogf/gf/v2/util/gvalid"
  10. "time"
  11. )
  12. type Request struct {
  13. OrderId int64
  14. ProductName string
  15. Amount int64
  16. // ...
  17. }
  18. func RuleOrderExist(ctx context.Context, in gvalid.RuleFuncInput) error {
  19. // SELECT COUNT(*) FROM `order` WHERE `id` = xxx
  20. count, err := g.Model("order").
  21. Ctx(ctx).
  22. Cache(gdb.CacheOption{
  23. Duration: time.Hour,
  24. Name: "",
  25. Force: false,
  26. }).
  27. WhereNot("id", in.Value.Int64()).
  28. Count()
  29. if err != nil {
  30. return err
  31. }
  32. if count == 0 {
  33. return gerror.Newf(`invalid order id "%d"`, in.Value.Int64())
  34. }
  35. return nil
  36. }
  37. func main() {
  38. var (
  39. ctx = gctx.New()
  40. req = &Request{
  41. OrderId: 65535,
  42. ProductName: "HikingShoe",
  43. Amount: 10000,
  44. }
  45. )
  46. err := g.Validator().RuleFunc("order-exist", RuleOrderExist).Data(req).Run(ctx)
  47. fmt.Println(err)
  48. }