多数据校验 - CheckMap

多数据校验即支持同时对多条数据进行校验,需要给定校验规则,并且可以自定义出错时的错误信息。 其中比较重要且复杂的是校验规则参数的定义。

校验方法: https://godoc.org/github.com/gogf/gf/util/gvalid

  1. // 自定义错误信息: map[键名] => 字符串|map[规则]错误信息
  2. type CustomMsg = map[string]interface{}
  3. func CheckMap(ctx context.Context, params interface{}, rules interface{}, msgs ...CustomMsg) Error
  1. 其中params参数支持任意 map 数据类型;
  2. rules参数支持 []string / map[string]string 数据类型,前面一种类型支持返回校验结果顺序,后一种不支持(因为map是无序的);
  3. rules参数中的 map[string]string 是一个二维的关联数组/哈希表,第一维键名为参数键名,第二维为带有错误的校验规则名称,键值为错误信息;
  4. msgs参数为自定义的错误信息,为非必需参数,类型为CustomMsgmap[string]interface{})具体使用请参考后续示例;

示例1,默认错误提示

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/frame/g"
  5. "github.com/gogf/gf/util/gvalid"
  6. )
  7. func main() {
  8. params := map[string]interface{}{
  9. "passport": "",
  10. "password": "123456",
  11. "password2": "1234567",
  12. }
  13. rules := map[string]string{
  14. "passport": "required|length:6,16",
  15. "password": "required|length:6,16|same:password2",
  16. "password2": "required|length:6,16",
  17. }
  18. err := gvalid.CheckMap(context.TODO(), params, rules)
  19. // 也可以使用链式操作
  20. // err := g.Validator().Rules(rules).CheckMap(params)
  21. if err != nil {
  22. g.Dump(err.Maps())
  23. }
  24. }

执行后,终端输出:

  1. {
  2. "passport": {
  3. "length": "The passport value length must be between 6 and 16",
  4. "required": "The passport field is required"
  5. },
  6. "password": {
  7. "same": "The password value must be the same as field password2"
  8. }
  9. }

示例2,自定义错误提示

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/frame/g"
  5. "github.com/gogf/gf/util/gvalid"
  6. )
  7. func main() {
  8. params := map[string]interface{}{
  9. "passport": "",
  10. "password": "123456",
  11. "password2": "1234567",
  12. }
  13. rules := map[string]string{
  14. "passport": "required|length:6,16",
  15. "password": "required|length:6,16|same:password2",
  16. "password2": "required|length:6,16",
  17. }
  18. messages := map[string]interface{}{
  19. "passport": "账号不能为空|账号长度应当在:min到:max之间",
  20. "password": map[string]string{
  21. "required": "密码不能为空",
  22. "same": "两次密码输入不相等",
  23. },
  24. }
  25. err := gvalid.CheckMap(context.TODO(), params, rules, messages)
  26. // 也可以使用链式操作
  27. // err := g.Validator().Messages(messages).Rules(rules).CheckMap(params)
  28. if err != nil {
  29. g.Dump(err.Maps())
  30. }
  31. }

该示例同时也展示了messsages自定义错误信息传递的两种数据类型,string或者map[string]string。其中map[string]string类型参数需要指定对应字段、对应规则的错误提示信息,是一个二维的“关联数组”。该示例执行后,终端输出:

  1. {
  2. "passport": {
  3. "length": "账号长度应当在6到16之间",
  4. "required": "账号不能为空"
  5. },
  6. "password": {
  7. "same": "两次密码输入不相等"
  8. }
  9. }

校验结果顺序性

如果将上面的例2程序多执行几次之后会发现,返回的结果是没有排序的,而且字段及规则输出的先后顺序完全是随机的。即使我们使用FirstItem, FirstString()等其他方法获取校验结果也是一样,返回的校验结果不固定。

那是因为校验的规则我们传递的是map类型,而golangmap类型并不具有有序性,因此校验的结果和规则一样是随机的,同一个校验结果的同一个校验方法多次获取结果值返回的可能也不一样了。

使用gvalid tag顺序性校验:

我们来改进一下以上的示例:校验结果中如果不满足required那么返回对应的错误信息,否则才是后续的校验错误信息;也就是说,返回的错误信息应当和我设定规则时的顺序一致。如下:

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/util/gvalid"
  6. )
  7. func main() {
  8. params := map[string]interface{}{
  9. "passport": "",
  10. "password": "123456",
  11. "password2": "1234567",
  12. }
  13. rules := []string{
  14. "passport@required|length:6,16#账号不能为空|账号长度应当在:min到:max之间",
  15. "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
  16. "password2@required|length:6,16#",
  17. }
  18. err := gvalid.CheckMap(context.TODO(), params, rules)
  19. // 也可以使用链式操作
  20. // err := g.Validator().Rules(rules).CheckMap(params)
  21. if err != nil {
  22. fmt.Println(err.Map())
  23. fmt.Println(err.FirstItem())
  24. fmt.Println(err.FirstString())
  25. }
  26. }

执行后,终端输出:

  1. map[length:账号长度应当在616之间 required:账号不能为空]
  2. passport map[length:账号长度应当在616之间 required:账号不能为空]
  3. 账号不能为空

可以看到,我们想要校验结果满足顺序性,只需要将rules参数的类型修改为[]string,按照一定的规则设定即可,并且msgs参数既可以定义到rules参数中,也可以分开传入(使用第三个参数)。rules的这种满足顺序性校验结果返回的规则,我们称之为gvalid tag,在后续的结构体对象校验中,我们也可以遇得到这种gvalid tag,语法是一样的。gvalid tag的书写规则如下:

  1. 键名名称@校验规则[#错误提示]

其中:

  • 键名名称 必需字段(如果结合struct使用为非必需字段),指需要校验的map中的键名名称,需要和params参数中的键名一致。
  • 校验规则 必需字段,多个规则以|符号连接,注意规则顺序需要和错误提示中的顺序一致,以便一一对应。
  • 错误提示 非必需字段,表示自定义的错误提示信息,当规则校验时对默认的错误提示信息进行覆盖;该字段也可以通过msgs参数传入覆盖。

注意这里的gvalid tag与后续章节结构体校验中的属性别名不同,属性别名为非必需参数,这里的键名名称为必需参数,这样在校验时才能指定校验的参数名称。

可选校验规则

当给定的数据校验规则中不包含required*规则时,表示该规则不是一个必须规则,当键值为nil或者空字符串时,将会忽略其校验。我们将之前的例子改一下,去掉passport键名的required规则:

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/util/gvalid"
  6. )
  7. func main() {
  8. params := map[string]interface{}{
  9. "passport": "",
  10. "password": "123456",
  11. "password2": "1234567",
  12. }
  13. rules := []string{
  14. "passport@length:6,16#账号不能为空|账号长度应当在:min到:max之间",
  15. "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等",
  16. "password2@required|length:6,16#",
  17. }
  18. err := gvalid.CheckMap(context.TODO(), params, rules)
  19. // 也可以使用链式操作
  20. // err := g.Validator().Rules(rules).CheckMap(params)
  21. if err != nil {
  22. fmt.Println(err.Map())
  23. fmt.Println(err.FirstItem())
  24. fmt.Println(err.FirstString())
  25. }
  26. }

执行后,终端输出:

  1. mmap[same:两次密码输入不相等]
  2. password map[same:两次密码输入不相等]
  3. 两次密码输入不相等

需要注意的是,如果键值为0或者false,将仍然会被校验。

Content Menu