校验组件支持强大的递归校验(嵌套校验)特性。如果给定的校验数据中的属性或者键值为 struct/map/slice 类型时,将会被自动执行递归校验。我们来看几个示例:

示例1,递归校验: struct

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. )
  7. type SearchReq struct {
  8. Key string `v:"required"`
  9. Option SearchOption
  10. }
  11. type SearchOption struct {
  12. Page int `v:"min:1"`
  13. Size int `v:"max:100"`
  14. }
  15. func main() {
  16. var (
  17. ctx = gctx.New()
  18. req = SearchReq{
  19. Key: "GoFrame",
  20. Option: SearchOption{
  21. Page: 1,
  22. Size: 10000,
  23. },
  24. }
  25. )
  26. err := g.Validator().Data(req).Run(ctx)
  27. fmt.Println(err)
  28. }

执行后,终端输出:

  1. The Size value `10000` must be equal or lesser than 100

示例2,递归校验: slice

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. )
  7. func main() {
  8. type Student struct {
  9. Name string `v:"required#Student Name is required"`
  10. Age int
  11. }
  12. type Teacher struct {
  13. Name string
  14. Students []Student
  15. }
  16. var (
  17. ctx = gctx.New()
  18. teacher = Teacher{}
  19. data = g.Map{
  20. "name": "john",
  21. "students": `[{"age":2},{"name":"jack", "age":4}]`,
  22. }
  23. )
  24. err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
  25. fmt.Println(err)
  26. }

执行后,终端输出:

  1. Student Name is required

示例3,递归校验: map

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. )
  7. func main() {
  8. type Student struct {
  9. Name string `v:"required#Student Name is required"`
  10. Age int
  11. }
  12. type Teacher struct {
  13. Name string
  14. Students map[string]Student
  15. }
  16. var (
  17. ctx = gctx.New()
  18. teacher = Teacher{
  19. Name: "Smith",
  20. Students: map[string]Student{
  21. "john": {Name: "", Age: 18},
  22. },
  23. }
  24. )
  25. err := g.Validator().Data(teacher).Run(ctx)
  26. fmt.Println(err)
  27. }

执行后,终端输出:

  1. Student Name is required

注意事项:空对象对递归校验的影响

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. )
  7. func main() {
  8. type Student struct {
  9. Name string `v:"required"`
  10. }
  11. type Teacher struct {
  12. Students Student
  13. }
  14. var (
  15. ctx = gctx.New()
  16. teacher = Teacher{}
  17. data = g.Map{
  18. "students": nil,
  19. }
  20. )
  21. err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
  22. fmt.Println(err)
  23. }

执行后,终端输出:

  1. Student Name is required

有同学可能会觉得奇怪,明明我都没有传递 Student 字段值,为什么还会递归校验 Student 结构体里面的 Name 字段?这是因为这里的 Student 属性是个空结构体,是带有默认值的( Name 默认值为空字符串)。递归校验里面,虽然 Student 不是必须参数,这个意思是你可以不传递,但是只要传递了就会按照里面属性的校验规则执行校验(带有默认值的空对象也算是有值)。可以对比和以下代码的差别:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. )
  7. func main() {
  8. type Student struct {
  9. Name string `v:"required"`
  10. }
  11. type Teacher struct {
  12. Students *Student
  13. }
  14. var (
  15. ctx = gctx.New()
  16. teacher = Teacher{}
  17. data = g.Map{
  18. "students": nil,
  19. }
  20. )
  21. err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
  22. fmt.Println(err)
  23. }

和前一示例的唯一差异在于 Student 属性从结构体改为了结构体指针 *Student,这样该属性不是空对象便没有默认值了。执行后,终端输出位空,表示校验通过。