结构体标签定义
通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签(Struct Tag)。结构体标签是对结构体字段的额外信息标签。 Tag是结构体在编译阶段关联到成员的元信息字符串,在运行的时候通过反射的机制读取出来。 结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔,具体的格式如下:
代码语言:javascript复制`key1:"value1" key2:"value2" key3:"value3"...` // 键值对用空格分隔
key会指定反射的解析方式包含 json(JSON标签)、 orm(Beego标签)、gorm(GORM标签)、bson(MongoDB标签)、form(表单标签)、binding(表单验证标签).这些系统使用标签设定字段在处理时应该具备的特殊属性和可能发生的行为。这些信息都是静态的,无须实例化结构体,可以通过反射获取到。
json标签
JSON数组可以用于编码Go语言的数组和slice。 将Go语言中结构体slice转为JSON的过程叫编组(marshaling),编组通过json.Marshal函数完成。 如果在结构体slice编码成JSON的时候使用自定义的成员名,可以使用结构体成员Tag来实现。 示例:
代码语言:javascript复制type User1 struct {
Name string `json:"username"` // 编码后的字段名为 username
Age int `json:"userage"` // 编码后的字段名为 userage
Sex string `json:"usersex"` // 编码后的字段名为 usersex
}
type User2 struct {
Name string `json:"username"`
Age int `json:"userage"`
Sex string // 编码后的字段名为 Sex
}
type User3 struct {
Name string `json:"username"`
Age int `json:"-"` // 字段不进行序列化 Sex string
}
func main() {
u1 := User1{"ares", 20, "man"}
jsondata1, err := json.Marshal(u1)
if err != nil {
fmt.Println("格式错误")
} else {
fmt.Printf("User1结构体转json:%sn", jsondata1)
}
u2 := User2{"ares", 20, "man"}
jsondata2, err := json.Marshal(u2)
if err != nil {
fmt.Println("格式错误")
} else {
fmt.Printf("User2结构体转json:%sn", jsondata2)
}
u3 := User3{"ares", 20, "man"}
jsondata3, err := json.Marshal(u3)
if err != nil {
fmt.Println("格式错误")
} else {
fmt.Printf("User3结构体转json:%sn", jsondata3)
}
}
输出为:
代码语言:javascript复制User1结构体转json:{"username":"ares","userage":20,"usersex":"man"}
User2结构体转json:{"username":"ares","userage":20,"Sex":"man"}
User3结构体转json:{"username":"ares","Sex":"man"}
"encoding/json"包的json.Marshal()方法作用就是把结构体转换为json,它读取了User结构体里面的标签,json键值对的键为定义的标签名,结构体的名字起了辅助作用,同时定义了字段数据类型。json.Unmarshal()可以把json字符串转换为结构体,在很多第三方包方法都会读取结构体标签。
标签选项:
标签选项 | 使用说明 |
---|---|
- | 字段不进行序列化 例:json:"-" |
omitempy | 类型零值或空值,序列化时忽略该字段 例:json:",omitempy" 字段名省略的话用结构体字段名 |
Type | 重新指定字段类型 例:json:"age,string" |
gorm标签
模型是标准的 struct,由基本数据类型以及实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成。 GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。 GORM 默认定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt,可以嵌套入自建结构体,tag名大小写不敏感,建议使用camelCase风格,多个标签定义用分号(;)分隔:
代码语言:javascript复制// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
建表示例: 结构体定义如下:
代码语言:javascript复制type AddUserAuth struct {
gorm.BaseModel
UUID string `gorm:"column:user_uuid;comment:用户UUID;type:varchar(100);"` // 用户UUID
User string `gorm:"column:user_name;comment:用户名称;type:varchar(50);"` // 用户登录名
Cluster string `gorm:"column:cluster_name;comment:集群名称;type:varchar(50);"` // k8s集群
NameSpace string `gorm:"column:namespace;comment:命名空间;type:varchar(50);"` // 命名空间
ServiceName string ` gorm:"column:service_name;comment:应用名称;type:varchar(50);"` // 应用名称
ServiceType string `gorm:"column:service_type;comment:应用类型;type:varchar(50);"` // 应用类型
}
生成的建表语句如下:
代码语言:javascript复制CREATE TABLE `add_user_auths` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`user_uuid` varchar(100) DEFAULT NULL COMMENT '用户UUID',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名称',
`cluster_name` varchar(50) DEFAULT NULL COMMENT '集群名称',
`namespace` varchar(50) DEFAULT NULL COMMENT '命名空间',
`service_name` varchar(50) DEFAULT NULL COMMENT '应用名称',
`service_type` varchar(50) DEFAULT NULL COMMENT '应用类型',
PRIMARY KEY (`id`),
KEY `idx_add_user_auths_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
使用 GORM Migrator 创建表时,不会创建被忽略的字段。 如果想要保存 UNIX(毫/纳)秒时间戳,而不是 time,只需简单地将 time.Time 修改为 int 即可。
字段标签
声明 model 时,tag 是可选的,GORM 支持以下 tag:
标签名 | 说明 |
---|---|
column | 指定 db 列名 |
type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT |
size | 指定列大小,例如:size:256 |
primaryKey | 指定列为主键 |
unique | 指定列为唯一 |
default | 指定列的默认值 |
precision | 指定列的精度 |
scale | 指定列大小 |
not null | 指定列为 NOT NULL |
autoIncrement | 指定列为自动增长 |
autoIncrementIncrement | 自动步长,控制连续记录之间的间隔 |
embedded | 嵌套字段 |
embeddedPrefix | 嵌入字段的列名前缀 |
autoCreateTime | 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano |
autoUpdateTime | 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli |
index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 |
uniqueIndex | 与 index 相同,但创建的是唯一索引 |
check | 创建检查约束,例如 check:age > 13,查看 约束 获取详情 |
<- | 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限 |
-> | 设置字段读的权限,->:false 无读权限 |
- | 忽略该字段,- 无读写权限 |
comment | 迁移时为字段添加注释 |
关联标签
GORM 允许通过标签为关联配置外键、约束、many2many 表:
标签名 | 说明 |
---|---|
foreignKey | 指定当前模型的列作为连接表的外键 |
references | 指定引用表的列名,其将被映射为连接表外键 |
polymorphic | 指定多态类型,比如模型名 |
polymorphicValue | 指定多态值、默认表名 |
many2many | 指定连接表表名 |
joinForeignKey | 指定连接表的外键列名,其将被映射到当前表 |
joinReferences | 指定连接表的外键列名,其将被映射到引用表 |
constraint | 关系约束,例如:OnUpdate、OnDelete |
form标签
Gin中提供了模型绑定,将表单数据和模型进行绑定,方便参数校验和使用。 模型绑定:
代码语言:javascript复制// 表单数据
type LoginForm struct {
UserName string `form:"username"`
Password string `form:"password"`
Email string `form:"email"`
}
// model 或 service 层Model
type Email struct {
Email string
Password string
}
func EmailLogin (c *gin.Context) {
var email LoginForm
if err := c.ShouldBind(&email); err != nil {
...
}
// 获取表单数据局
args := Email {
Email: email.Email,
Password: email.Password,
}
// 对参数进行后续使用
...
}
通过 form:"email" 对表单email数据进行绑定。然后通过Bind()、ShouldBind()等方法获取参数值。
binding标签
Gin 主要提供了两组绑定方法 Must bind 与 Should bind 。gin/binding 内置模型绑定实现,将请求数据提取到合适的绑定器。 Must bind:验证不通过,就会被终止或抛出特定的错误页面 Should bind:存在绑定错误,这个错误会被返回,需要自行处理相应的请求和错误。 Gin 框架本身已经实现了多种绑定,通常用来绑定来自请求数据,有不同的结构体实例与之对应。其实现的绑定有 JSON, XML, Form,Query,FormPost,FormMultipart,ProtoBuf,MsgPack,YAML,Uri。 Gin对于数据的校验使用的是 validator.v10 包,该包提供多种数据校验方法,通过binding:""标签来进行数据校验。 校验规则见github:validator 示例:
代码语言:javascript复制type LoginForm struct {
Email string `form:"email" binding:"email"`
UserName string `form:"username" binding:"username"`
Password string `form:"password" binging:"required,min=6,max=10"`
}
特殊符号:
符号 | 说明 |
---|---|
, | 分隔多个标签选项,逗号之间不能有空格 |
- | 该字段不做校验 |
| | 使用多个选项,满足其中一个即可 |
必须校验
标签选项 | 说明 | 示例 |
---|---|---|
required | 表示该字段值必输设置,且不能为默认值 | binding:required |
omitempty | 如果字段未设置,则忽略它 | binding:reqomitemptyuired |
字符串校验
标签选项 | 使用说明 | 示例 |
---|---|---|
contains | 参数值包含设置子串 | binding:"contains=ares"是否包含ares字符串 |
excludes | 参数值不包含设置子串 | binding:"excludes=ares"是否不包含ares字符串 |
startswith | 字符串前缀 | binding:"startswith=ares"是否以tom开头 |
endswith | 字符串前缀 | binding:"endswith=ares"是否以tom结尾 |
范围校验
范围验证: 切片、数组和map、字符串,验证其长度;数值,验证大小范围。
标签选项 | 使用说明 | 示例 |
---|---|---|
len | 参数值等于给定值 | binding:"len=3"等于3 |
ne | 不等于 | binding:"ne=3"不等于3 |
max | 最大值,小于等于参数值 | binding:"max=3"小于等于3 |
min | 最小值,大于等于参数值 | binding:"min=3"大于等于3 |
lte | 参数值小于等于给定值 | binding:"lte=3"小于等于3 |
gte | 参数值大于等于给定值 | binding:"gte=3"大于等于3 |
lt | 参数值小于给定值 | binding:"lt=3"小于3 |
gt | 参数值大于给定值 | binding:"gt=3"大于3 |
oneof | 参数值只能是枚举值中的一个,值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围 | binding:"oneof=red green" |
字段校验
标签选项 | 使用说明 |
---|---|
eqcsfield | 跨不同结构体字段相等,比如struct1 field1 是否等于struct2 field2 |
necsfield | 跨不同结构体字段不相等 |
eqfield | 同一结构体字段相等验证,例如:输入两次密码 |
nefield | 同一结构体字段不相等验证 |
gtefield | 大于等于同一结构体字段 |
ltefield | 小于等于同一结构体字段 |
示例:
代码语言:javascript复制// 不同结构体校验
type S1 struct {
F1 string `validate:eqcsfield=S2.F2`
S2 struct {
F2 string
}
}
// 同一结构体字段相同校验
type Email struct {
Email string `validate:"lte=4"`
Pwd string `validate:"min=10"`
Pwd2 string `validate:"eqfield=Pwd"`
}
// 同一结构体字段不相等
type User struct {
Name string `validate:"lte=4"`
Age int `validate:"min=20"`
Password string `validate:"min=10,nefield=Name"`
}
其他校验
标签选项 | 使用说明 | 示例 |
---|---|---|
ip | 合法IP地址校验 | binding:"ip" |
合法邮箱校验 | binding:"email" | |
url | 合法的URL | binding:"url" |
uri | 合法的URI | binding:"uri" |
uuid | uuid验证 | binding:"uuid" |
datetime | 合法时间格式值校验 | binding:"datetime=2006-01-02" |
json | JSON数据验证 | validate:"json" |
numeric | 数值验证 正则:^[- ]?[0-9] (?:\.[0-9] )?$ | validate:"numeric" |
number | 整数验证 正则:^[0-9] $ | validate:"number" |
alpha | 字母字符串验证 正则:^[a-zA-Z] $ | validate:"alpha" |
alphanum | 字母数字字符串验证 正则:^[a-zA-Z0-9] $ | validate:"alphanum" |
ascii | Ascii 字符验证 | validate:"ascii" |
ini标签
ini 是 Windows 上常用的配置文件格式, go-ini是 Go 语言中用于操作 ini 文件的第三方库。 若使用ini格式配置,需要将配置文件字段映射到结构体变量,如果键名与字段名不相同,那么需要在结构标签中指定对应的键名。标准库encoding/json、encoding/xml解析时可以将键名直接对应到字段名,而go-ini库不可以,所以需要在结构体标签指定对应键名。 示例:
代码语言:javascript复制## 配置文件 cnf.ini
user_name = ares
age = 20
// 配置文件映射 结构体
type Config struct {
UserName string `ini:"user_name"` // ini标签指定下键名
Age string `ini:"age"`
}