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í)超過1秒則認(rèn)為失敗。排查發(fā)現(xiàn),可能是gin接收解析json數(shù)據(jù)存在耗時(shí),代碼使用ctx.ShouldBindJSON直接解析得到所需結(jié)構(gòu)體,然后通過自實(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
}
從上述過程看到,先是調(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)體。
也可以通過ioutil.ReadAll(ctx.Request.Body)先讀取客戶端來的數(shù)據(jù),由于約定為json格式數(shù)據(jù),所以可以用json.Unmarshal解析成結(jié)構(gòu)體。
無法哪種方法,其實(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)文章
Go語言的Windows下環(huán)境配置以及簡(jiǎn)單的程序結(jié)構(gòu)講解
這篇文章主要介紹了Go語言的Windows下環(huán)境配置以及簡(jiǎn)單的程序結(jié)構(gòu)講解,從編程語言約定俗成的hellow world開始,需要的朋友可以參考下2015-10-10
看看你的Go應(yīng)用是否用了正確CPU核數(shù)
這篇文章主要為大家介紹了Go應(yīng)用正確的CPU核數(shù)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

