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

Golang?中的?json?編解碼深度解析

 更新時(shí)間:2025年10月19日 08:59:39   作者:Innsane  
本文帶領(lǐng)大家重新認(rèn)識(shí)Golang中的json編解碼,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

json 是我的老朋友,上份工作開(kāi)發(fā) web 應(yīng)用時(shí)就作為前后端數(shù)據(jù)交流的協(xié)議,現(xiàn)在也是用 json 數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)。雖然面熟得很但還遠(yuǎn)遠(yuǎn)達(dá)不到知根知底,而且在邊界的探索上越發(fā)束手束腳。比如之前想寫(xiě)一個(gè)范型的結(jié)構(gòu)提高通用性,但是不清楚對(duì)范型的支持如何,思來(lái)想去還是用了普通類(lèi)型;還有項(xiàng)目中的規(guī)范不允許使用指針類(lèi)型的字段存儲(chǔ),我一直抱有疑問(wèn)。歸根結(jié)底還是不熟悉 json 編解碼的一些特性,導(dǎo)致我不敢嘗試也不敢使用,生怕出了問(wèn)題。所以近些日子也是狠狠研究了一把,補(bǔ)習(xí)了很多之前模棱兩可的概念。

有一句話(huà)說(shuō)的好:“多和舊人做新事”,我想我和 json 大概也屬于這種關(guān)系吧(?)

json 解析時(shí)字段名稱(chēng)保持一致

這個(gè)疑問(wèn)是,假如我們編碼不太規(guī)范,不給字段添加 Tag,序列化和反序列化后的字段字符串會(huì)是什么?

type Object struct {
	ID      string
	VaLuE2T int64
}
func TestFunc(t *testing.T) {
	obj := Object{
		ID:      "the-id",
		VaLuE2T: 7239,
	}
	marshal, err := json.Marshal(obj)
	assert.Nil(t, err)
	fmt.Println(string(marshal))
}
{"ID":"the-id","VaLuE2T":7239}

用代碼驗(yàn)證的結(jié)果是,json 編碼并不會(huì)將程序中定義的字段名稱(chēng)改成駝峰或者什么特殊大小寫(xiě)規(guī)則,而是完完全全使用原本的字符。如果是我目前的這個(gè)需求,即僅用來(lái)保存數(shù)據(jù),編碼和解碼都在后端進(jìn)行,那這樣完全可用不需要考慮更多,但如果是需要前后端數(shù)據(jù)對(duì)齊,而且有特殊的字段名稱(chēng)規(guī)范,那就要使用 tag 對(duì)編碼字段進(jìn)行規(guī)定,比如下方的代碼。

type Object struct {
	ID      string `json:"id"`
	VaLuE2T int64  `json:"value2t"`
}
func TestFunc(t *testing.T) {
	obj := Object{
		ID:      "the-id",
		VaLuE2T: 7239,
	}
	marshal, err := json.Marshal(obj)
	assert.Nil(t, err)
	fmt.Println(string(marshal))
}
{"id":"the-id","value2t":7239}

但這只是編碼,對(duì)于解碼來(lái)說(shuō),是大小寫(xiě)不敏感的,就算傳過(guò)來(lái)的是某種形式的妖魔鬼怪也可以解析出來(lái),比如

type Object struct {
	CaSeTesT string
	CAsEteSt string
}
func TestFunc(t *testing.T) {
	newObj := Object{}
	testString := `{"cAsEteSt":"test"}`
	err := json.Unmarshal([]byte(testString), &newObj)
	assert.Nil(t, err)
	fmt.Println("CaSeTesT:", newObj.CaSeTesT, " CAsEteSt:", newObj.CAsEteSt)
}
CaSeTesT: test  CAsEteSt: 

也因?yàn)槿绱?,最好不要在相關(guān)結(jié)構(gòu)體里定義名稱(chēng)相同的字段,即便有大小寫(xiě)的區(qū)別,也會(huì)導(dǎo)致不可預(yù)料的情況發(fā)生。而且嚴(yán)格按照駝峰格式命名的話(huà),不存在大小寫(xiě)區(qū)別,相同字母的字段就是唯一的。

而 Go 團(tuán)隊(duì)也將在 json/v2 中默認(rèn)大小寫(xiě)敏感,規(guī)范的行為肯定會(huì)帶來(lái)更少的 bug ~ 關(guān)于 json/v2 具體可以參考:A new experimental Go API for JSON。

哦哦還有一點(diǎn),如果不想某個(gè)字段參與解碼編碼可以使用特殊的 tag。

type Object struct {
	Value string `json:"-"`
}

可以編解碼接口和范型

我們知道 json 官方包底層是依靠反射實(shí)現(xiàn)的,所以獲取到傳入接口的結(jié)構(gòu)體類(lèi)型不是問(wèn)題,就可以使用原結(jié)構(gòu)體類(lèi)型去編解碼,所以只要是 Golang 支持的類(lèi)型都可以,甚至是范型。當(dāng)然也有一些反例需要注意,比如 func 這種類(lèi)型就不行。

type Object struct {
	Func func()
}
func TestFunc(t *testing.T) {
	obj := Object{
		Func: func() {},
	}
	marshal, err := json.Marshal(obj)
	fmt.Println(err)
}
json: unsupported type: func()

omitempty 和字段類(lèi)型

  • 當(dāng)字段是結(jié)構(gòu)體類(lèi)型的,那么 omitempty 無(wú)效。
  • 當(dāng)字段是指針類(lèi)型的,如果值是 nil,那么有 omitempty 就不進(jìn)行編碼,沒(méi)有 omitempty 會(huì)編碼成 null。
  • 經(jīng)過(guò)測(cè)試不僅是指針類(lèi)型的結(jié)構(gòu)體,指針類(lèi)型的基礎(chǔ)類(lèi)型比如 string 或者 int64 也是如此。
type Object struct {
	TheStructO AObject  `json:"theStructO,omitempty"`
	TheStruct  AObject  `json:"theStruct"`
	ThePointO  *AObject `json:"thePointO,omitempty"`
	ThePoint   *AObject `json:"thePoint"`
}
type AObject struct {
	Values interface{}
}
func TestFunc(t *testing.T) {
	obj := Object{}
	marshal, err := json.Marshal(obj)
	assert.Nil(t, err)
	fmt.Println(string(marshal))
}
{"theStructO":{"Values":null},"theStruct":{"Values":null},"thePoint":null}

結(jié)構(gòu)體類(lèi)型和指針類(lèi)型性能比較

使用 Benchmark 測(cè)試結(jié)構(gòu)體類(lèi)型和指針類(lèi)型的性能。結(jié)論是在 CPU 性能上兩者差不多,但是一個(gè)指針類(lèi)型的字段會(huì)多進(jìn)行一次內(nèi)存分配,在一定程度上增加了 GC 的壓力,所以看起來(lái)小的結(jié)構(gòu)體還是結(jié)構(gòu)體值類(lèi)型更合適。

type ObjectStruct struct {
	TheStruct AObject `json:"theStruct"`
}
type ObjectPoint struct {
	TheStruct *AObject `json:"theStruct"`
}
func BenchmarkFunc(b *testing.B) {
	data := []byte(`{"theStruct":{"valueString":"text","valueInt":123,"valueFloat":3.14}}`)
	b.Run("unmarshal-struct", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_ = json.Unmarshal(data, &ObjectStruct{})
		}
	})
	b.Run("unmarshal-point", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_ = json.Unmarshal(data, &ObjectPoint{})
		}
	})
}
BenchmarkFunc
BenchmarkFunc/unmarshal-struct
BenchmarkFunc/unmarshal-struct-8  	  457996	 2518 ns/op	 304 B/op	 8 allocs/op
BenchmarkFunc/unmarshal-point
BenchmarkFunc/unmarshal-point-8    	  471489	 2517 ns/op	 312 B/op	 9 allocs/op
PASS

自定義 json 編解碼方式

可以實(shí)現(xiàn) json 規(guī)定的接口,使結(jié)構(gòu)體執(zhí)行特定的編解碼方式,假設(shè)下面一種情況,我希望業(yè)務(wù)代碼開(kāi)發(fā)中使用方便查詢(xún)和操作的map,然后存儲(chǔ)或者通訊使用占用空間更少的數(shù)組或者切片,但同時(shí)我又不想增加開(kāi)發(fā)人員的心智負(fù)擔(dān),想要之前怎么使用現(xiàn)在就如何使用,或者無(wú)法更改一些庫(kù)的執(zhí)行方式只能繞路。也就是說(shuō)平時(shí)開(kāi)發(fā)時(shí)需要直接調(diào)用 json.Marshal 或 json.UnMarshal,而不需要額外操作,這時(shí)就可以通過(guò)實(shí)現(xiàn)接口的方式達(dá)成目的,見(jiàn)如下代碼。

type Object struct {
	UserMap map[string]struct{}
}
func (o Object) MarshalJSON() ([]byte, error) {
	list := make([]string, 0, len(o.UserMap))
	for key := range o.UserMap {
		list = append(list, key)
	}
	return json.Marshal(list)
}
func (o *Object) UnmarshalJSON(b []byte) error {
	var list []string
	err := json.Unmarshal(b, &list)
	if err != nil {
		return err
	}
	o.UserMap = make(map[string]struct{}, len(list))
	for i := range list {
		o.UserMap[list[i]] = struct{}{}
	}
	return nil
}
type ObjectNormal struct {
	UserMap map[string]struct{}
}
func TestFunc(t *testing.T) {
	userMap := map[string]struct{}{
		"user1": {},
		"user2": {},
		"user3": {},
	}
	obj1 := &Object{
		UserMap: userMap,
	}
	obj2 := &ObjectNormal{
		UserMap: userMap,
	}
	marshal1, err := json.Marshal(obj1)
	assert.Nil(t, err)
	fmt.Println("len:", len(marshal1), string(marshal1))
	marshal2, err := json.Marshal(obj2)
	assert.Nil(t, err)
	fmt.Println("len:", len(marshal2), string(marshal2))
}
len: 25 ["user1","user2","user3"]
len: 46 {"UserMap":{"user1":{},"user2":{},"user3":{}}}

此處還有一個(gè)小 Tips,UnmarshalJSON 用指針接收器沒(méi)問(wèn)題,因?yàn)樾枰薷恼{(diào)用這個(gè)方法的結(jié)構(gòu)體的字段值,但是 MarshalJSON 盡量用值接收器,因?yàn)檫@樣在調(diào)用 json.Marshal 時(shí)無(wú)論傳入的是值還是指針都能正常編碼,同時(shí)也避免了傳入的是 nil 導(dǎo)致 panic。

被遺忘在角落的 gob

在 golang 源碼的 encoding 包下有很多編解碼方式,比如 json、xml、base64 等等,但其中也有一個(gè) gob,假如你之前沒(méi)有接觸過(guò) golang 這門(mén)編程語(yǔ)言那你大概率沒(méi)有聽(tīng)說(shuō)過(guò)這種編碼解碼方式,因?yàn)樗酮?dú)屬于 golang,其他語(yǔ)言基本上可以說(shuō)無(wú)法解析。

type G struct {
	Value string
}
func TestGOB(t *testing.T) {
	g := &G{Value: "hello"}
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(g); err != nil {
		panic(err)
	}
	fmt.Println("Gob encoded bytes:", buf.Bytes())
	var decoded G
	dec := gob.NewDecoder(&buf)
	if err := dec.Decode(&decoded); err != nil {
		panic(err)
	}
	fmt.Println("Decoded struct:", decoded)
}

使用方式大差不差,但與 json 的行為相比需要依賴(lài) bytes.Buffer,也正因如此可以連續(xù)向 Buffer 編碼多個(gè)結(jié)構(gòu)體,然后連續(xù)解碼多個(gè)結(jié)構(gòu)體。此外和 json 一樣也可以實(shí)現(xiàn)特定的接口來(lái)自定義編解碼行為,具體可以參考https://pkg.go.dev/encoding/gob。

向 json 和 xml 這種編碼方式方便讓我們?nèi)庋塾^(guān)察,但因此也犧牲了性能和空間,而 gob 類(lèi)似 protobuf 都是生成二進(jìn)制,但是 gob 僅存在于 golang 生態(tài)中,普及度遠(yuǎn)遠(yuǎn)不及可以生成多種語(yǔ)言代碼的 protobuf。

type User struct {
	Name string
}
func Benchmark(b *testing.B) {
	b.Run("gob", func(b *testing.B) {
		var buf bytes.Buffer
		enc := gob.NewEncoder(&buf)
		dec := gob.NewDecoder(&buf)
		user := User{Name: "hello"}
		for i := 0; i < b.N; i++ {
			_ = enc.Encode(user)
			_ = dec.Decode(&user)
		}
	})
	b.Run("json", func(b *testing.B) {
		user := User{Name: "hello"}
		for i := 0; i < b.N; i++ {
			marshal, _ := json.Marshal(user)
			_ = json.Unmarshal(marshal, &user)
		}
	})
	b.Run("protobuf", func(b *testing.B) {
		user := ttt.User{Name: "hello"}
		for i := 0; i < b.N; i++ {
			data, _ := proto.Marshal(&user)
			_ = proto.Unmarshal(data, &user)
		}
	})
}

控制變量法,我設(shè)計(jì)了相同的結(jié)構(gòu)體 proto。

message User {
  string Name = 1;
}
Benchmark
Benchmark/gob
Benchmark/gob-8         	 1230975	      954.7 ns/op	      32 B/op	       3 allocs/op
Benchmark/json
Benchmark/json-8        	 1000000	      1130 ns/op	     256 B/op	       7 allocs/op
Benchmark/protobuf
Benchmark/protobuf-8    	 2500924	      483.2 ns/op	      16 B/op	       2 allocs/op
PASS

可能是由于我用的是簡(jiǎn)單結(jié)構(gòu)體,gob 和 json 在 CPU 性能上并沒(méi)有看到什么差距,但是內(nèi)存分配差了蠻多,如果不考慮通用性和擴(kuò)展性的話(huà),gob 也是個(gè)不錯(cuò)的選擇,雖然事實(shí)是這兩方面不可能不考慮。而且在性能方面也遠(yuǎn)遠(yuǎn)不及代碼生成派,生產(chǎn)實(shí)踐中多多用 protobuf 才是正道。

RawMessage 的應(yīng)用場(chǎng)景

試想這樣一種情況,某個(gè)推薦業(yè)務(wù)有兩層分別是 A 和 B ,通常是是 A 調(diào)用 B 的接口(RPC),然后 A 再組織數(shù)據(jù)發(fā)給前端,QA和運(yùn)營(yíng)需求要獲取到 B 持有的信息用來(lái) debug 和測(cè)試,這個(gè)時(shí)候因?yàn)槭遣魂P(guān)鍵的 debug 信息所以也就懶得定義消息結(jié)構(gòu)體,而是直接在B中用 json 將數(shù)據(jù)序列化成字符串傳給 A,然后 A 在外面封裝一層錯(cuò)誤碼和數(shù)據(jù)傳給前端,如果直接這么操作會(huì)有一個(gè)問(wèn)題:

type ResponseB struct {
	Name string
}
type ResponseA struct {
	Data string
}
func TestRaw(t *testing.T) {
	r := ResponseB{
		Name: "hello-world",
	}
	marshal, err := json.Marshal(r)
	assert.Nil(t, err)
	ra := &ResponseA{
		Data: string(marshal),
	}
	marshal2, err := json.Marshal(ra)
	assert.Nil(t, err)
	fmt.Println(string(marshal), string(marshal2))
}
{"Name":"hello-world"} {"Data":"{\"Name\":\"hello-world\"}"}

字符串類(lèi)型的字段在 json.Marshal 時(shí),其中的雙引號(hào)會(huì)被轉(zhuǎn)義,甚至于三層四層來(lái)回傳遞后轉(zhuǎn)移符號(hào)會(huì)越來(lái)越多。所以這個(gè)時(shí)候就可以使用 json.RawMessage。

type ResponseB struct {
	Name string
}
type ResponseA struct {
	Data json.RawMessage
}
func TestRaw(t *testing.T) {
	r := RawStruct{
		Name: "hello-world",
	}
	marshal, err := json.Marshal(r)
	assert.Nil(t, err)
	rj := &RawJson{
		Data: json.RawMessage(marshal),
	}
	marshal3, err := json.Marshal(rj)
	assert.Nil(t, err)
	fmt.Println(string(marshal), string(marshal3))
}
{"Name":"hello-world"} {"Data":{"Name":"hello-world"}}

除了編碼之外,解碼時(shí)的 RawMessage 也有大用處,尤其是需要二次解碼的情況。比如有一個(gè)接口是聊天室發(fā)送消息,然后消息有不同的類(lèi)型,每個(gè)類(lèi)型的內(nèi)容的結(jié)構(gòu)都不一樣,這時(shí)需要先解碼通用結(jié)構(gòu),然后拿到消息類(lèi)型,再根據(jù)消息類(lèi)型解碼具體消息內(nèi)容。比如下面這個(gè)例子,如果不使用 RawMessage,就一定要在字符串內(nèi)增加轉(zhuǎn)義。

type Inside struct {
	Name string
}
type Outside struct {
	Data       interface{}
	DataString string
	DataRaw    json.RawMessage
}
func TestRaw(t *testing.T) {
	data := `{"Data":"{"Name":"hello-world"}","DataString":"{"Name":"hello-world"}","DataRaw":{"Name":"hello-world"}}`
	rj := Outside{}
	err := json.Unmarshal([]byte(data), &rj)
	assert.Nil(t, err)
	fmt.Println(rj)
}
Expected nil, but got: &json.SyntaxError{msg:"invalid character 'N' after object key:value pair", Offset:12}

新時(shí)代的明星 json v2

從 https://pkg.go.dev/encoding/json?tab=versions 中可以看到,json 包在 go1 也就是最初的版本就已經(jīng)存在了,只是當(dāng)時(shí)有一些設(shè)計(jì)和特性放到當(dāng)下來(lái)看是有些老舊的,由于 Go 的兼容性承諾也不便對(duì)其進(jìn)行大刀闊斧的改動(dòng),正是因?yàn)槿绱耍谧罱陌姹局?go 團(tuán)隊(duì)推出了新的 json 包也就是 json/v2 來(lái)解決 json 編解碼的一些痛點(diǎn)問(wèn)題。如果對(duì)具體內(nèi)容感興趣可以去閱讀官方的文檔 https://pkg.go.dev/encoding/json/v2,包括 v1 版本和 v2 版本的一些區(qū)別 https://pkg.go.dev/encoding/json#hdr-Migrating_to_v2,以及介紹新版本 json 的博客 [https://go.dev/blog/jsonv2-exp](A new experimental Go API for JSON)。

會(huì)用 v2 實(shí)現(xiàn) v1,只是 v1 中原本的一些特性在 v2 中會(huì)變成可選擇的 Option 提供出來(lái)以保證兼容性,這些選項(xiàng)不乏上文提到的一些特殊性質(zhì),譬如:

  • 編解碼結(jié)構(gòu)體時(shí)字段大小寫(xiě)敏感 (case-sensitive)
  • omitempty 起作用的對(duì)象會(huì)發(fā)生變化
  • nil 的 slice 和 map 會(huì)編碼成空數(shù)組和空結(jié)構(gòu)體而不是 null
  • 以及其他的一些性質(zhì)

當(dāng)然不只是一些編解碼行為發(fā)生了變化,性能方面也有了很大提高,甚至還能看到專(zhuān)門(mén)的文章介紹和分析當(dāng)前社區(qū)流行的諸多 json 庫(kù)和 json/v2 的對(duì)比,老熟人 sonic 也在其中,具體內(nèi)容詳見(jiàn) [https://github.com/go-json-experiment/jsonbench](JSON Benchmarks)。

到此這篇關(guān)于重新認(rèn)識(shí) Golang 中的 json 編解碼的文章就介紹到這了,更多相關(guān)Golang 中的 json 編解碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang編寫(xiě)自定義IP限流中間件的方法詳解

    Golang編寫(xiě)自定義IP限流中間件的方法詳解

    這篇文章給大家詳細(xì)的介紹了Golang編寫(xiě)自定義IP限流中間件的方法,文章通過(guò)代碼實(shí)例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-09-09
  • golang實(shí)現(xiàn)通過(guò)smtp發(fā)送電子郵件的方法

    golang實(shí)現(xiàn)通過(guò)smtp發(fā)送電子郵件的方法

    這篇文章主要介紹了golang實(shí)現(xiàn)通過(guò)smtp發(fā)送電子郵件的方法,實(shí)例分析了Go語(yǔ)言基于SMTP協(xié)議發(fā)送郵件的相關(guān)技巧,需要的朋友可以參考下
    2016-07-07
  • Go設(shè)計(jì)模式之原型模式圖文詳解

    Go設(shè)計(jì)模式之原型模式圖文詳解

    原型模式是一種創(chuàng)建型設(shè)計(jì)模式, 使你能夠復(fù)制已有對(duì)象, 而又無(wú)需使代碼依賴(lài)它們所屬的類(lèi),本文將通過(guò)圖片和文字讓大家可以詳細(xì)的了解Go的原型模式,感興趣的通過(guò)跟著小編一起來(lái)看看吧
    2023-07-07
  • Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過(guò)程詳解

    Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過(guò)程詳解

    這篇文章主要介紹了Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過(guò)程詳解,大概思路是在Go的結(jié)構(gòu)體中每個(gè)屬性打上一個(gè)excel標(biāo)簽,利用反射獲取標(biāo)簽中的內(nèi)容,作為表格的Header,需要的朋友可以參考下
    2022-06-06
  • golang標(biāo)準(zhǔn)庫(kù)SSH操作示例詳解

    golang標(biāo)準(zhǔn)庫(kù)SSH操作示例詳解

    文章介紹了如何使用Golang的crypto/ssh庫(kù)實(shí)現(xiàn)SSH客戶(hù)端功能,包括連接遠(yuǎn)程服務(wù)器、執(zhí)行命令、捕獲輸出以及與os/exec標(biāo)準(zhǔn)庫(kù)的對(duì)比,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • Gin框架自帶參數(shù)校驗(yàn)的使用詳解

    Gin框架自帶參數(shù)校驗(yàn)的使用詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用Gin框架自帶的參數(shù)校驗(yàn),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下
    2023-09-09
  • Go 日志封裝實(shí)戰(zhàn)示例詳解

    Go 日志封裝實(shí)戰(zhàn)示例詳解

    這篇文章主要為大家介紹了Go 日志封裝實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Golang實(shí)現(xiàn)讀取ZIP壓縮包并顯示Gin靜態(tài)html網(wǎng)站

    Golang實(shí)現(xiàn)讀取ZIP壓縮包并顯示Gin靜態(tài)html網(wǎng)站

    這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Golang實(shí)現(xiàn)從ZIP壓縮包讀取內(nèi)容并作為Gin靜態(tài)網(wǎng)站顯示,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-07-07
  • Go實(shí)現(xiàn)JWT認(rèn)證中間件的項(xiàng)目實(shí)戰(zhàn)

    Go實(shí)現(xiàn)JWT認(rèn)證中間件的項(xiàng)目實(shí)戰(zhàn)

    本文將介紹在Gin框架中實(shí)現(xiàn)完整的JWT認(rèn)證方案,同時(shí)包含靈活的?Redis?集成選項(xiàng),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08
  • 15個(gè)Golang中時(shí)間處理的實(shí)用函數(shù)

    15個(gè)Golang中時(shí)間處理的實(shí)用函數(shù)

    在Go編程中,處理日期和時(shí)間是一項(xiàng)常見(jiàn)任務(wù),涉及到精確性和靈活性,本文將介紹一系列實(shí)用函數(shù),它們充當(dāng)time包的包裝器,需要的可以參考下
    2024-01-01

最新評(píng)論