Golang中g(shù)in框架綁定解析json數(shù)據(jù)的兩種方法
本文介紹 Golang 的 gin 框架接收json數(shù)據(jù)并解析的2種方法。
起因及排查
某微服務(wù)工程,最近測(cè)試發(fā)現(xiàn)請(qǐng)求超時(shí),由于特殊原因超時(shí)較短,如果請(qǐng)求處理耗時(shí)超過(guò)1秒則認(rèn)為失敗。排查發(fā)現(xiàn),可能是gin接收解析json數(shù)據(jù)存在耗時(shí),代碼使用ctx.ShouldBindJSON直接解析得到所需結(jié)構(gòu)體,然后通過(guò)自實(shí)現(xiàn)的FormatJsonStruct函數(shù)格式化并輸出到日志。該格式函數(shù)如下:
func FormatJsonStruct(str interface{}, format bool) (ret string) { ret = "" jsonstr, err := json.Marshal(str) if err != nil { return } if format { var out bytes.Buffer _ = json.Indent(&out, []byte(jsonstr), "", " ") ret = out.String() } else { ret = string(jsonstr) } return }
從上述過(guò)程看到,先是調(diào)用了ShouldBindJSON
,再調(diào)用了Marshal
函數(shù)解析成字符串。于是考慮調(diào)用ReadAll
讀取數(shù)據(jù),再用Unmarshal
解析成結(jié)構(gòu)體,直接輸出結(jié)構(gòu)體數(shù)據(jù)。下面模擬2種不同的解析josn方法。
模擬程序
本節(jié)結(jié)合代碼,簡(jiǎn)單描述模擬程序。詳見文附錄。
一般地,在gin中,業(yè)務(wù)處理函數(shù)帶有*gin.Context參數(shù),如本文的HandleGinShouldBindJSON,使用ctx.ShouldBindJSON(&request)將ctx中帶的數(shù)據(jù)直接轉(zhuǎn)換成目標(biāo)結(jié)構(gòu)體。
也可以通過(guò)ioutil.ReadAll(ctx.Request.Body)先讀取客戶端來(lái)的數(shù)據(jù),由于約定為json格式數(shù)據(jù),所以可以用json.Unmarshal解析成結(jié)構(gòu)體。
無(wú)法哪種方法,其實(shí)都很方便,相對(duì)而言,前者更便捷。
測(cè)試結(jié)果
使用curl模擬請(qǐng)求命令,示例如下:
curl http://127.0.0.1:9000/foo -X POST -H "Content-Type:application/json" -d '{"id":"test_001", "op":"etc", "timestamp":12342134341234, "data":{"name":"foo", "addr":"bar", "code":450481, "age":100}}' curl http://127.0.0.1:9000/bar -X POST -H "Content-Type:application/json" -d '{"id":"test_001", "op":"etc", "timestamp":12342134341234, "data":{"name":"foo", "addr":"bar", "code":450481, "age":100}}'
服務(wù)端輸出日志:
=== RUN TestGin test of gin run gin [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] POST /foo --> webdemo/test/gin_test.HandleGinShouldBindJSON (1 handlers) [GIN-debug] POST /bar --> webdemo/test/gin_test.HandleGinUnmarshal (1 handlers) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. [GIN-debug] Listening and serving HTTP on :9000 ShouldBindJSON: request: #{test_001 etc 12342134341234 {foo bar 450481 100}} Unmarshal request: #{test_001 etc 12342134341234 {foo bar 450481 100}} exit status 0xc000013a
小結(jié)
就目前測(cè)試和修改結(jié)果看,本文所述方法并非主因,真正原因待查。
附完整代碼
/* 結(jié)構(gòu)體 { "id": "test_001", "op": "etc", "timestamp": 12342134341234, "data": { "name": "foo", "addr": "bar", "code": 450481, "age": 100 } } curl http://127.0.0.1:9000/foo -X POST -H "Content-Type:application/json" -d '{"id":"test_001", "op":"etc", "timestamp":12342134341234, "data":{"name":"foo", "addr":"bar", "code":450481, "age":100}}' curl http://127.0.0.1:9000/bar -X POST -H "Content-Type:application/json" -d '{"id":"test_001", "op":"etc", "timestamp":12342134341234, "data":{"name":"foo", "addr":"bar", "code":450481, "age":100}}' */ package test import ( "encoding/json" "fmt" "io/ioutil" "strings" "testing" "github.com/gin-gonic/gin" ) var g_port string = "9000" type MyRequest_t struct { Id string `json:"id"` Op string `json:"op"` Timestamp int `json:"timestamp"` Data ReqData_t `json:"data"` } type ReqData_t struct { Name string `json:"name"` Addr string `json:"addr"` Code int `json:"code"` Age int `json:"age"` } func routerPost(r *gin.Engine) { r.POST("/foo", HandleGinShouldBindJSON) r.POST("/bar", HandleGinUnmarshal) } func initGin() { fmt.Println("run gin") router := gin.New() routerPost(router) router.Run(":" + g_port) } func HandleGinShouldBindJSON(ctx *gin.Context) { var request MyRequest_t var err error ctxType := ctx.Request.Header.Get("Content-Type") if strings.Contains(ctxType, "application/json") { // 純 json // 先獲取總的json if err = ctx.ShouldBindJSON(&request); err != nil { fmt.Printf("ShouldBindJSON failed: %v\n", err) return } fmt.Printf("ShouldBindJSON: request: #%v\n", request) } else { fmt.Println("非json") return } } func HandleGinUnmarshal(ctx *gin.Context) { var request MyRequest_t var err error var reqbuffer []byte ctxType := ctx.Request.Header.Get("Content-Type") if strings.Contains(ctxType, "application/json") { // 純 json reqbuffer, err = ioutil.ReadAll(ctx.Request.Body) if err != nil { fmt.Printf("ReadAll body failed: %v\n", err) return } err = json.Unmarshal(reqbuffer, &request) if err != nil { fmt.Printf("Unmarshal to request failed: %v\n", err) return } fmt.Printf("Unmarshal request: #%v\n", request) } else { fmt.Println("非json") return } } func TestGin(t *testing.T) { fmt.Println("test of gin") initGin() }
以上就是Golang中g(shù)in框架綁定解析json數(shù)據(jù)的兩種方法的詳細(xì)內(nèi)容,更多關(guān)于Golang gin綁定解析json的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang在GRPC中設(shè)置client的超時(shí)時(shí)間
這篇文章主要介紹了golang在GRPC中設(shè)置client的超時(shí)時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Go語(yǔ)言pointer及switch?fallthrough實(shí)戰(zhàn)詳解
這篇文章主要為大家介紹了Go語(yǔ)言pointer及switch?fallthrough實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06golang string、int、int64 float 互相轉(zhuǎn)換方式
這篇文章主要介紹了golang string、int、int64 float 互相轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07