結構體標簽定義
通過 reflect.Type 獲取結構體成員信息 reflect.StructField 結構中的 Tag 被稱為結構體標簽(Struct Tag)。結構體標簽是對結構體字段的額外信息標簽。
Tag是結構體在編譯階段關聯到成員的元信息字符串,在運行的時候通過反射的機制讀取出來。
結構體標簽由一個或多個鍵值對組成。鍵與值使用冒號分隔,值用雙引號括起來。鍵值對之間使用一個空格分隔,具體的格式如下:
`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來實現。
示例:
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:%s\n", jsondata1)
}
u2 := User2{"ares", 20, "man"}
jsondata2, err := json.Marshal(u2)
if err != nil {
fmt.Println("格式錯誤")
} else {
fmt.Printf("User2結構體轉json:%s\n", jsondata2)
}
u3 := User3{"ares", 20, "man"}
jsondata3, err := json.Marshal(u3)
if err != nil {
fmt.Println("格式錯誤")
} else {
fmt.Printf("User3結構體轉json:%s\n", jsondata3)
}
}
輸出為:
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風格,多個標簽定義用分號(;)分隔:
// gorm.Model 的定義
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
建表示例:
結構體定義如下:
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);"` // 應用類型
}
生成的建表語句如下:
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中提供了模型綁定,將表單數據和模型進行綁定,方便參數校驗和使用。
模型綁定:
// 表單數據
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
示例:
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 | 小於等於同一結構體字段 |
示例:
// 不同結構體校驗
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庫不可以,所以需要在結構體標簽指定對應鍵名。
示例:
## 配置文件 cnf.ini
user_name = ares
age = 20
// 配置文件映射 結構體
type Config struct {
UserName string `ini:"user_name"` // ini標簽指定下鍵名
Age string `ini:"age"`
}