Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Children Display
alltrue
excerptTypesimple

多数据校验 -  CheckStruct

CheckStruct的使用方式同CheckMap,除了第一个参数为struct类型的结构体对象(也可以是对象指针)。

Note

注意哟,大家常问到得一个问题。struct的属性会有默认值,在某些情况下会引起required规则的失效,因此根据实际情况配合多种规则(例如min规则)一起校验会是一个比较严谨的做法。此外,也可以将属性改为指针类型,例如*int*float64等。

接口文档: https://godoc.org/github.com/gogf/gf/util/gvalid

Code Block
languagego
func CheckStruct(ctx context.Context, object interface{}, rules interface{}, msgs ...CustomMsg) Error

校验方法也可以使用校验对象的链式操作方法来替代,包方法CheckStruct等同于以下链式操作方式:

Code Block
languagego
g.Validator().Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckStruct(object)

示例1,使用map指定规则及提示信息

Code Block
languagego
package main

import (
	"context"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gvalid"
)

func main() {
	type User struct {
		Age  int
		Name string
	}
	rules := map[string]string{
		"Name": "required|length:6,16",
		"Age":  "between:18,30",
	}
	messages := map[string]interface{}{
		"Name": map[string]string{
			"required": "名称不能为空",
			"length":   "名称长度为:min到:max个字符",
		},
		"Age": "年龄为18到30周岁",
	}
	user := User{Name: "john"}
	err := gvalid.CheckStruct(context.TODO(), user, rules, messages)
	// 也可以使用链式操作
	// err := g.Validator().Rules(rules).Messages(messages).CheckStruct(user)
	if err != nil {
		g.Dump(err.Maps())
	}
}

在以上示例中,Age属性由于默认值0的存在,因此会引起required规则的失效,因此这里没有使用required规则而是使用between规则来进行校验。示例代码执行后,终端输出:

Code Block
languagejs
{
	"Age": {
		"between": "年龄为18到30周岁"
	},
	"Name": {
		"length": "名称长度为6到16个字符"
	}
}

示例2,使用gvalid tag绑定规则及错误信息

使用gvalid tag设置的规则,其校验结果是顺序性的。

Code Block
languagego
package main

import (
	"context"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gvalid"
)

type User struct {
	Uid   int    `v:"uid      @integer|min:1#|请输入用户ID"`
	Name  string `v:"name     @required|length:6,30#请输入用户名称|用户名称长度非法"`
	Pass1 string `v:"password1@required|password3"`
	Pass2 string `v:"password2@required|password3|same:Pass2#|密码格式不合法|两次密码不一致,请重新输入"`
}

func main() {
	user := &User{
		Name:  "john",
		Pass1: "Abc123!@#",
		Pass2: "123",
	}

	// 使用结构体定义的校验规则和错误提示进行校验
	if e := gvalid.CheckStruct(context.TODO(), user, nil); e != nil {
		g.Dump(e.Items())
	}

	// 自定义校验规则和错误提示,对定义的特定校验规则和错误提示进行覆盖
	rules := map[string]string{
		"uid": "min:6",
	}
	messages := map[string]interface{}{
		"password2": map[string]string{
			"password3": "名称不能为空",
		},
	}
	err := gvalid.CheckStruct(context.TODO(), user, rules, messages)
	// 也可以使用链式操作
	// err := g.Validator().Rules(rules).Messages(messages).CheckStruct(user)
	if err != nil {
		g.Dump(err.Items())
	}
}

可以看到,我们可以对在struct定义时使用了gvalid的标签属性(gvalid tag)来绑定校验的规则及错误提示信息,规则如下:

[属性别名@]校验规则[#错误提示]

可以看到,CheckStructCheckMapgvalid tag规则是一样的,不过在字段的含义上有一点点小区别:

  • 属性别名错误提示非必需字段校验规则必需字段。
  • 属性别名 非必需字段,指定在校验中使用的对应struct属性的别名,同时校验成功后的map中的也将使用该别名返回,例如在处理请求表单时比较有用,因为表单的字段名称往往和struct的属性名称不一致。
  • 错误提示 非必需字段,表示自定义的错误提示信息,当规则校验时对默认的错误提示信息进行覆盖;

在此示例代码中,same:password1规则同使用same:Pass2规则是一样的效果。也就是说,在数据校验中,可以同时使用原有的struct属性名称,也可以使用别名。但是,返回的结果中只会使用别名返回,这也是别名最大的用途。此外,在使用CheckStruct方法对struct对象进行校验时,也可以传递校验或者和错误提示参数,这个时候会覆盖struct在定义时绑定的对应参数。

以上示例执行后,输出结果为:

Code Block
languagexml
[
    {
        "uid": {
            "min": "请输入用户ID"
        }
    },
    {
        "name": {
            "length": "用户名称长度非法"
        }
    },
    {
        "password2": {
            "password3": "密码格式不合法"
        }
    }
] [
    {
        "name": {
            "length": "用户名称长度非法"
        }
    },
    {
        "password2": {
            "password3": "名称不能为空"
        }
    }
]

示例3,结构体嵌套校验

CheckStruct支持嵌套的(embedded)结构体校验,即如果属性也是结构体,并且结构体的属性带有gvalid标签,无论多深的嵌套层级,这些属性都将被根据设定的规则进行校验。

使用示例:

Code Block
languagego
package main

import (
	"context"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gvalid"
)

func main() {
	type Pass struct {
		Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"`
		Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"`
	}
	type User struct {
		Pass
		Id   int
		Name string `valid:"name@required#请输入您的姓名"`
	}
	user := &User{
		Name: "john",
		Pass: Pass{
			Pass1: "1",
			Pass2: "2",
		},
	}
	err := gvalid.CheckStruct(context.TODO(), user, nil)
	// 也可以使用链式操作
	// err := g.Validator().CheckStruct(user)
	g.Dump(err.Maps())
}

执行后,终端输出:

{
	"password1": {
		"same": "您两次输入的密码不一致"
	},
	"password2": {
		"same": "您两次输入的密码不一致"
	}
}

多数据校验 -  CheckStructWithData

为了避免结构体默认值带来的困惑,从goframe v1.16版本开始,我们增加了一个新的结构体校验方法CheckStructWithData,用于结构体校验时严格按照给定的map参数而不是按照结构体的属性值,而校验规则同样会自动读取结构体中的gvalid tag。我们来看一个示例,我们将之前的示例进行简单的修改:

Code Block
languagego
package main

import (
	"context"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/gvalid"
)

func main() {
	type User struct {
		Name string `v:"required#请输入用户姓名"`
		Type int    `v:"required#请选择用户类型"`
	}
	data := g.Map{
		"name": "john",
	}
	user := User{}
	if err := gconv.Scan(data, &user); err != nil {
		panic(err)
	}
	err := gvalid.CheckStructWithData(context.TODO(), user, data, nil)
	// 也可以使用链式操作
	// err := g.Validator().Data(data).CheckStruct(user)
	if err != nil {
		g.Dump(err.Items())
	}
}

执行后,终端输出:

Code Block
languagejs
[
    {
        "Type": {
            "required": "请选择用户类型"
        }
    }
]

可以看到,结构体中的属性Type校验规则required并没有受到默认值的影响,仍旧被执行了预期的校验检查。

可选校验规则

当给定的数据校验规则中不包含required*规则时,表示该规则不是一个必须规则,当属性值为nil或者空字符串时,将会忽略其校验。

示例1,空字符串

type Params struct {
	Page      int    `v:"required|min:1         # page is required"`
	Size      int    `v:"required|between:1,100 # size is required"`
	ProjectId string `v:"between:1,10000        # project id must between :min, :max"`
}
obj := &Params{
	Page: 1,
	Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
fmt.Println(err)
// Output:
// <nil>

示例2,空指针属性

type Params struct {
	Page      int       `v:"required|min:1         # page is required"`
	Size      int       `v:"required|between:1,100 # size is required"`
	ProjectId *gvar.Var `v:"between:1,10000        # project id must between :min, :max"`
}
obj := &Params{
	Page: 1,
	Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
fmt.Println(err)
// Output:
// <nil>

示例3,空整型属性

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

type Params struct {
	Page      int `v:"required|min:1         # page is required"`
	Size      int `v:"required|between:1,100 # size is required"`
	ProjectId int `v:"between:1,10000        # project id must between :min, :max"`
}
obj := &Params{
	Page: 1,
	Size: 10,
}
err := gvalid.CheckStruct(context.TODO(), obj, nil)
fmt.Println(err)
// Output:
// project id must between 1, 10000

...

titleContent Menu

...