欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go基于struct?tag實(shí)現(xiàn)結(jié)構(gòu)體字段級(jí)別的訪問控制

 更新時(shí)間:2024年02月01日 14:28:31   作者:波羅學(xué)?碼途漫漫  
本文將會(huì)基于這個(gè)主題展開,討論Go中的結(jié)構(gòu)體tag究竟是什么,我們?cè)撊绾卫盟?另外,文末還提供了一個(gè)實(shí)際案例,實(shí)現(xiàn)結(jié)構(gòu)體字段級(jí)別的訪問,幫助我們進(jìn)一步提升對(duì)struct tag的理解

struct tag 是什么?

在Go 中,結(jié)構(gòu)體主要是用于定義復(fù)雜數(shù)據(jù)類型,而 struct tag 則是附加在 struct 字段后的字符串,提供了一種方式來存儲(chǔ)關(guān)于字段的元信息,然后,tag 在程序運(yùn)行時(shí)一般不會(huì)直接影響程序邏輯,

如下是一個(gè)定義了 tag 的結(jié)構(gòu)體 Person 類型。

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

例子中,json:"name"json:"age" 就是結(jié)構(gòu)體 tag。結(jié)構(gòu)體 tag 的使用非常直觀。你只需要在定義結(jié)構(gòu)體字段后,通過反引號(hào) `` 包裹起來的鍵值對(duì)形式就可定義它們。

具體有什么用呢?

這個(gè) tag 究竟有什么用呢?為何要定義它們。

單從這個(gè)例子中來看,假設(shè)你是在 "encoding/json" 庫中使用 Person 結(jié)構(gòu)體,它是告訴 Go 在處理 JSON 序列化和反序列化時(shí),字段名稱的轉(zhuǎn)化規(guī)則。

讓我們通過它在 "encoding/json" 的使用說明它的效果吧。

p := Person{Name: "John", Age: 30}
jsonData, err := json.Marshal(p)
if err != nil {
    log.Println(err)
}
fmt.Println(string(jsonData)) 

輸出:

{"name":"John","age":30}

可以看到輸出的 JSON 的 key 是 name 和 age,而非 Name 和 Age。

與其他語言對(duì)比的話,雖然 Go 的 struct tag 在某種程度上類似于 Java 的注解或 C# 的屬性,但 Go 的 tag 更加簡(jiǎn)潔,并且主要通過反射機(jī)制在運(yùn)行時(shí)被訪問。

這種設(shè)計(jì)反映了Go語言的哲學(xué):簡(jiǎn)單、直接而有效。但確實(shí)也是功能有限!

常見使用場(chǎng)景

結(jié)構(gòu)體 tag 在 Go 語言中常見用途,我平時(shí)最常見有如下這些。

JSON/XML 序列反序列化

如前面的介紹的案例中,通過 encoding/json 或者其他的庫如 encoding/xml 庫,tag 可以控制如何將結(jié)構(gòu)體字段轉(zhuǎn)換為 JSON 或 XML,或者如何從它們轉(zhuǎn)換回來。

數(shù)據(jù)庫操作

在ORM(對(duì)象關(guān)系映射)庫中,tag 可以定義數(shù)據(jù)庫表的列名、類型或其他特性。

如我們?cè)谑褂?Gorm 時(shí),會(huì)看到這樣的定義:

type User struct {
    gorm.Model
    Name   string `gorm:"type:varchar(100);unique_index"`
    Age    int    `gorm:"index:age"`
    Active bool   `gorm:"default:true"`
}

結(jié)構(gòu)體 tag 可用于定義數(shù)據(jù)庫表的列名、類型或其他特性。

數(shù)據(jù)驗(yàn)證

在一些庫中,tag 用于驗(yàn)證數(shù)據(jù),例如,確保一個(gè)字段是有效的電子郵件地址。

如下是 govalidator[1] 使用結(jié)構(gòu)體上 tag 實(shí)現(xiàn)定義數(shù)據(jù)驗(yàn)證規(guī)則的一個(gè)案例。

type User struct {
    Email string `valid:"email"`
    Age   int    `valid:"range(18|99)"`
}

在這個(gè)例子中,valid tag 定義了字段的驗(yàn)證規(guī)則,如 email 字段值是否是有效的 email,age 字段是否滿足數(shù)值在 18 到 99 之間等。

我們只要將類型為 User 類型的變量交給 govalidator,它可以根據(jù)這些規(guī)則來驗(yàn)證數(shù)據(jù),確保數(shù)據(jù)的正確性和有效性。

示例如下:

valid, err := govalidator.ValidateStruct(User{Email: "test@example.com", Age: 20})

返回的 valid: 為 true 或 false,如果發(fā)生錯(cuò)誤,err 提供具體的錯(cuò)誤原因。

tag 行為自定義

前面展示的都是利用標(biāo)準(zhǔn)庫或三方庫提供的能力,如果想自定義 tag 該如何實(shí)現(xiàn)?畢竟有些情況下,如果默認(rèn)提供的 tag 提供的能力不滿足需求,我們還是希望可以自定義 tag 的行為。

這需要了解與理解 Go 的反射機(jī)制,它為數(shù)據(jù)處理和元信息管理提供了強(qiáng)大的靈活性。

如下的示例代碼:

type Person struct {
    Name string `mytag:"MyName"`
}

t := reflect.TypeOf(Person{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("mytag")) // 輸出: MyName

在這個(gè)例子中,我們的 Person 的字段 Name 有一個(gè)自定義的 tag - mytag,我們直接通過反射就可以訪問它。

這只是簡(jiǎn)單的演示如何訪問到 tag。如何使用它呢?

這就要基于實(shí)際的場(chǎng)景了,當(dāng)然,這通常也離不開與反射配合。下面我們來通過一個(gè)實(shí)際的例子介紹。

案例:結(jié)構(gòu)體字段訪問控制

讓我們考慮一個(gè)實(shí)際的場(chǎng)景:一個(gè)結(jié)構(gòu)訪問控制系統(tǒng)。

這個(gè)系統(tǒng)中,我們可以根據(jù)用戶的角色(如 admin、user)或者請(qǐng)求的來源(admin、web)控制對(duì)結(jié)構(gòu)體字段的訪問。具體而言,假設(shè)我定義了一個(gè)包含敏感信息的結(jié)構(gòu)體,我可以使用自定義 tag 來標(biāo)記每個(gè)字段的訪問權(quán)限。

是不是想到,這或許可用在 API 接口范圍字段的控制上,防止泄露敏感數(shù)據(jù)給用戶。

接下來,具體看看如何做吧?

定義結(jié)構(gòu)體

我們首先定義一個(gè)UserProfile結(jié)構(gòu)體,其中包含用戶的各種信息。每個(gè)信息字段都有一個(gè)自定義的 access tag,用于標(biāo)識(shí)字段訪問權(quán)限(adminuser)。

type UserProfile struct {
    Username    string `access:"user"`  // 所有用戶可見
    Email       string `access:"user"`  // 所有用戶可見
    PhoneNumber string `access:"admin"` // 僅管理員可見
    Address     string `access:"admin"` // 僅管理員可見
}

其中,PhoneNumber 和 Address 是敏感字段,它只對(duì) admin 角色可見。而 UserName 和 Email 則是所有用戶可見。

到此,結(jié)構(gòu)體 UserProfile 定義完成。

實(shí)現(xiàn)權(quán)限控制

接下來就是要實(shí)現(xiàn)一個(gè)函數(shù),實(shí)現(xiàn)根據(jù) UserProfile 定義的 access tag 決定字段內(nèi)容的可見性。

假設(shè)函數(shù)名稱為 FilterFieldsByRole,它接受一個(gè) UserProfile 類型變量和用戶角色,返回內(nèi)容一個(gè)過濾后的 map(由 fieldname 到 fieldvalue 組成的映射),其中只包含角色有權(quán)訪問的字段。

func FilterFieldsByRole(profile UserProfile, role string) map[string]string {
    result := make(map[string]string)
    val := reflect.ValueOf(profile)
    typ := val.Type()

    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        accessTag := field.Tag.Get("access")
        if accessTag == "user" || accessTag == role {
            // 獲取字段名稱
            fieldName := strings.ToLower(field.Name) 
            // 獲取字段值
            fieldValue := val.Field(i).String() 
            // 組織返回結(jié)果 result
            result[fieldName] = fieldValue
        }
    }
    return result
}

權(quán)限控制的重點(diǎn)邏輯部分,就是 if accessTag == "user" || accessTag == role 這段判斷條件。當(dāng)滿足條件之后,接下來要做的就是通過反射獲取字段名稱和值,并組織目標(biāo)的 Map 類變量 result了。

使用演示

讓我們來使用下 FilterFieldsByRole 函數(shù),檢查下是否滿足按角色訪問特定的用戶信息的功能。

示例代碼如下:

func main() {
    profile := UserProfile{
        Username:    "johndoe",
        Email:       "johndoe@example.com",
        PhoneNumber: "123-456-7890",
        Address:     "123 Elm St",
    }

    // 假設(shè)當(dāng)前用戶是普通用戶
    userInfo := FilterFieldsByRole(profile, "user")
    fmt.Println(userInfo)

    // 假設(shè)當(dāng)前用戶是管理員
    adminInfo := FilterFieldsByRole(profile, "admin")
    fmt.Println(adminInfo)
}

輸出:

map[username:johndoe email:johndoe@example.com]
map[username:johndoe email:johndoe@example.com phonenumber:123-456-7890 address:123 Elm St]

這個(gè)場(chǎng)景,通過自定義結(jié)構(gòu)體 tag,給予指定角色,很輕松地就實(shí)現(xiàn)了一個(gè)基于角色的權(quán)限控制。

毫無疑問,這個(gè)代碼更加清晰和可維護(hù),而且具有極大靈活性、擴(kuò)展性。如果想擴(kuò)展更多角色,也是更加容易。

不過還是要說明下,如果在 API 層面使用這樣的能力,還是要考慮反射可能帶來的性能影響。

總結(jié)

這篇博文介紹了Go語言中結(jié)構(gòu)體 tag 的基礎(chǔ)知識(shí),如是什么,如何使用。另外,還介紹了它們?cè)诓煌瑘?chǎng)景下的應(yīng)用。通過簡(jiǎn)單的例子和對(duì)比,我們看到了 Go 中結(jié)構(gòu)體 tag 的作用。

文章的最后,通過一個(gè)實(shí)際案例,演示了如何使用 struct tag 使我們代碼更加靈活強(qiáng)大。雖然 struct tag 的使用非常直觀,但正確地利用這些 tag 可以極大提升我們程序的功能和效率

引用鏈接

[1] govalidator: github.com/asaskevich/govalidator

以上就是Go基于struct tag實(shí)現(xiàn)結(jié)構(gòu)體字段級(jí)別的訪問控制的詳細(xì)內(nèi)容,更多關(guān)于Go struct tag訪問控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解go語言中type關(guān)鍵詞的幾種使用

    詳解go語言中type關(guān)鍵詞的幾種使用

    這篇文章主要介紹了詳解go語言中type的幾種使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • golang 中的 nil的場(chǎng)景分析

    golang 中的 nil的場(chǎng)景分析

    這篇文章主要介紹了golang 中的 nil,本文通過多種場(chǎng)景分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Golang執(zhí)行cmd命令行的方法

    Golang執(zhí)行cmd命令行的方法

    本文主要介紹了Golang執(zhí)行cmd命令行的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 深度解密Go語言中字符串的使用

    深度解密Go語言中字符串的使用

    在編程語言中,字符串發(fā)揮著重要的角色。這篇文章就來帶大家一起深度解密Go語言中的字符串,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-09-09
  • Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    這篇文章主要介紹了Go項(xiàng)目的目錄結(jié)構(gòu),對(duì)基礎(chǔ)目錄做了講解,對(duì)項(xiàng)目開發(fā)中的其它目錄也一并做了介紹,需要的朋友可以參考下
    2014-10-10
  • Go實(shí)現(xiàn)Redis連接池方法

    Go實(shí)現(xiàn)Redis連接池方法

    為了更深入了解golang連接池的實(shí)現(xiàn),自已又重寫了一遍連接池。文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Golang+Vue輕松構(gòu)建Web應(yīng)用的方法步驟

    Golang+Vue輕松構(gòu)建Web應(yīng)用的方法步驟

    本文主要介紹了Golang+Vue輕松構(gòu)建Web應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • GoLand編寫 TCP 端口掃描器的詳細(xì)過程

    GoLand編寫 TCP 端口掃描器的詳細(xì)過程

    TCP,也就是傳輸控制協(xié)議(Transmission Control Protocol),這篇文章主要介紹了Go語言(Golang)編寫 TCP 端口掃描器,需要的朋友可以參考下
    2023-05-05
  • Go語言常用字符串處理方法實(shí)例匯總

    Go語言常用字符串處理方法實(shí)例匯總

    這篇文章主要介紹了Go語言常用字符串處理方法,實(shí)例匯總了Go語言中常見的各種字符串處理技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03
  • Go處理包含多種引號(hào)的字符串的幾種方法

    Go處理包含多種引號(hào)的字符串的幾種方法

    在Go中,有幾種方式可以處理包含多種引號(hào)的字符串,以確保代碼的可讀性和正確性,本文將給大家詳細(xì)介紹了這幾種處理方式,并通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04

最新評(píng)論