当我们将一个接口值传递给一个 reflect.ValueOf 函数调用时,此调用返回的是代表着此接口值的动态值的一个 reflect.Value 值。我们必须通过间接的途径获得一个代表一个接口值的 reflect.Value 值。
reflect.Value 类型有很多方法(Package reflect - The Go Programming Language)。我们可以调用这些方法来观察和操纵一个 reflect.Value 属主值表示的 Go 值。这些方法中的有些适用于所有种类类型的值,有些只适用于一种或几种类型的值。
通过不合适的 reflect.Value 属主值调用某个方法将在运行时产生一个恐慌。请阅读 reflect 代码库中各个方法的文档来获取如何正确地使用这些方法。
一个 reflect.Value 值的 CanSet 方法将返回此 reflect.Value 值代表的 Go 值是否可以被修改(可以被赋值)。如果一个 Go 值可以被修改,则我们可以调用对应的 reflect.Value 值的 Set 方法来修改此 Go 值。注意:reflect.ValueOf 函数直接返回的 reflect.Value 值都是不可修改的。
反射不仅可以获取值的类型信息,还可以动态地获取或者设置变量的值。Go语言中使用 reflect.Value 获取和设置变量的值。
使用反射值对象包装任意值
Go语言中,使用 reflect.ValueOf() 函数获得值的反射值对象(reflect.Value)。书写格式如下:
value := reflect.ValueOf(rawValue)
reflect.ValueOf 返回 reflect.Value 类型,包含有 rawValue 的值信息。reflect.Value 与原值间可以通过值包装和值获取互相转化。reflect.Value 是一些反射操作的重要类型,如反射调用函数。
从反射值对象获取被包装的值
Go语言中可以通过 reflect.Value 重新获得原始值。
1) 从反射值对象(reflect.Value)中获取值的方法
可以通过下面几种方法从反射值对象 reflect.Value 中获取原值,如下表所示。
方法名 | 说 明 |
---|---|
Interface() interface {} | 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 |
Int() int64 | 将值以 int 类型返回,所有有符号整型均可以此方式返回 |
Uint() uint64 | 将值以 uint 类型返回,所有无符号整型均可以此方式返回 |
Float() float64 | 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
Bool() bool | 将值以 bool 类型返回 |
Bytes() []bytes | 将值以字节数组 []bytes 类型返回 |
String() string | 将值以字符串类型返回 |
2) 从反射值对象(reflect.Value)中获取值的例子
下面代码中,将整型变量中的值使用 reflect.Value 获取反射值对象(reflect.Value)。再通过 reflect.Value 的 Interface() 方法获得 interface{} 类型的原值,通过 int 类型对应的 reflect.Value 的 Int() 方法获得整型值。
- package main
- import (
- "fmt"
- "reflect"
- )
- func main() {
- // 声明整型变量a并赋初值
- var a int = 1024
- // 获取变量a的反射值对象
- valueOfA := reflect.ValueOf(a)
- // 获取interface{}类型的值, 通过类型断言转换
- var getA int = valueOfA.Interface().(int)
- // 获取64位的值, 强制类型转换为int类型
- var getA2 int = int(valueOfA.Int())
- fmt.Println(getA, getA2)
- }
代码输出如下:
1024 1024
代码说明如下:
- 第 11 行,声明一个变量,类型为 int,设置初值为 1024。
- 第 14 行,获取变量 a 的反射值对象,类型为 reflect.Value,这个过程和 reflect.TypeOf() 类似。
- 第 17 行,将 valueOfA 反射值对象以 interface{} 类型取出,通过类型断言转换为 int 类型并赋值给 getA。
- 第 20 行,将 valueOfA 反射值对象通过 Int 方法,以 int64 类型取出,通过强制类型转换,转换为原本的 int 类型。
/****************************************************************************************/
一、概述
随着Golang在近年来的快速发展,很多开发者选择了它作为自己的首选编程语言。而在实际开发中,我们通常需要对数据进行一定的验证,以确保系统的稳定性和安全性。这时候,一个好用的字段验证器就显得尤为重要了。本文将详细介绍Golang工程组件之一的字段验证器validator的使用方法。
二、什么是validator
Validator是一个Golang的开源库,主要用于验证数据的合法性。它提供了丰富的验证规则,可以满足大部分场景下的需求。同时,Validator还支持自定义验证规则,你可以根据具体需求添加自己的验证规则。Validator已经被广泛应用于各种Golang项目中。
三、Validator的安装
在使用Validator之前,需要先安装它。在Golang中,可以使用go get命令轻松安装Validator:
go get -u github.com/go-playground/validator/v10
四、使用Validator
4.1 基本用法
Validator的使用非常简单,只需要引入包并创建一个新的验证器对象即可:
import "github.com/go-playground/validator/v10"
validate := validator.New()
接下来,就可以使用验证器对象的ValidateStruct方法验证结构体是否合法:
type User struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
}
user := &User{Name: "test", Email: "test@test.com"}
err := validate.Struct(user)
if err != nil {
// 验证失败
}
在上面的例子中,我们定义了一个名为User的结构体,并为它的Name和Email字段设置了验证规则。其中,validate:"required"表示该字段必须存在且不能为空;validate:"email"表示该字段必须是一个合法的邮箱地址。最后,我们使用验证器对象的Struct方法进行验证。
如果验证失败,会返回错误信息。否则,表示数据合法。
4.2 自定义验证规则
有时候,Validator提供的默认验证规则并不能满足我们的需求。这时候,我们就需要自定义验证规则了。Validator支持自定义验证规则,只需要实现validator.Func接口即可:
func myFunc(fl validator.FieldLevel) bool {
// 验证逻辑
return true
}
validate.RegisterValidation("my_rule", myFunc)
type User struct {
Age int `validate:"my_rule"`
}
user := &User{Age: 18}
err := validate.Struct(user)
if err != nil {
// 验证失败
}
在上面的例子中,我们定义了一个名为myFunc的函数作为自定义验证规则。这个函数需要实现validator.FieldLevel接口,接收一个参数fl,它包含了字段的相关信息,如字段名、值等。在函数中,我们可以编写自己的验证逻辑,并返回一个bool值表示验证结果。
接着,我们使用验证器对象的RegisterValidation方法注册我们定义的验证规则。"my_rule"是我们给这个规则起的名字。
最后,我们定义了一个User结构体,并为它的Age字段设置了我们自定义的验证规则。最终,我们在验证器对象上调用Struct方法进行验证即可。
4.3 错误处理
当数据验证失败时,Validator会返回一个ValidationError类型的错误。其中,Errors()方法可以返回所有验证失败的字段及其相关信息:
type User struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
}
user := &User{Name: "test", Email: "test"}
if err := validate.Struct(user); err != nil {
for _, err := range err.(validator.ValidationErrors) {
// 输出错误信息
fmt.Println(err.Field(), err.Tag())
}
}
在上面的例子中,数据验证失败了,我们使用for循环遍历所有的错误信息,并输出它们的字段名和相关规则(Tag)。
五、总结
本文简单介绍了Golang工程组件之一的字段验证器Validator的基本用法、自定义验证规则以及错误处理。Validator提供了丰富的验证规则,能够满足大部分场景下的需求,并支持自定义验证规则。同时,Validator还能够输出详细的错误信息,便于我们快速定位问题并解决。无疑,Validator是一个非常实用的工具,也是Golang开发过程中不可或缺的一部分。
/***************************************************************************************/
添加依赖
go get github.com/go-playground/validator
代码
package main
import (
"fmt"
"github.com/go-playground/validator"
)
var validate *validator.Validate //定义
type User struct {
Name string `validate:"required"` //非空
Age uint8 `validate:"gte=0,lte=130"` // 0<=Age<=130
Email string `validate:"required,email"` //非空,email格式
//dive关键字代表 进入到嵌套结构体进行判断
Address []*Address `validate:"dive"` // 可以拥有多个地址
}
type Address struct {
Province string `validate:"required"` //非空
City string `validate:"required"` //非空
Phone string `validate:"numeric,len=11"` //数字类型,长度为11
}
func main() {
validate = validator.New() //初始化(赋值)
validateStruct() //结构体校验
validateVariable() //变量校验
}
func validateStruct() {
address := Address{
Province: "重庆",
City: "重庆",
Phone: "13366663333x",
}
user := User{
Name: "江洲",
Age: 23,
Email: "jz@163.com",
Address: []*Address{&address},
}
err := validate.Struct(user)
if err != nil {
//断言为:validator.ValidationErrors,类型为:[]FieldError
for _, e := range err.(validator.ValidationErrors) {
fmt.Println("Namespace:", e.Namespace())
fmt.Println("Field:", e.Field())
fmt.Println("StructNamespace:", e.StructNamespace())
fmt.Println("StructField:", e.StructField())
fmt.Println("Tag:", e.Tag())
fmt.Println("ActualTag:", e.ActualTag())
fmt.Println("Kind:", e.Kind())
fmt.Println("Type:", e.Type())
fmt.Println("Value:", e.Value())
fmt.Println("Param:", e.Param())
fmt.Println()
}
fmt.Println("结构体输入数据类型错误!")
return
} else {
fmt.Println("结构体校验通过")
}
}
//变量校验
func validateVariable() {
myEmail := "123@qq.com" //邮箱地址:xx@xx.com
err := validate.Var(myEmail, "required,email")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("变量校验通过!")
}
}