Gin 框架中,處理 JSON 格式的參數綁定時,默認採用的標準包 encoding/json
,然而標準包不能知足咱們的一些要求,好比兼容字符串整型、PHP空數組、時間格式等。git
開發 API 時,須要用到 ShouldBindJSON 綁定傳入的參數到結構體:github
// github.com/gin-gonic/gin@v1.6.3/context.go:643 // ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON). func (c *Context) ShouldBindJSON(obj interface{}) error { return c.ShouldBindWith(obj, binding.JSON) }
Gin 默認採用 encoding/json
包:json
// github.com/gin-gonic/gin@v1.6.3/internal/json/json.go // +build !jsoniter package json import "encoding/json" var ( // Marshal is exported by gin/json package. Marshal = json.Marshal // Unmarshal is exported by gin/json package. Unmarshal = json.Unmarshal // MarshalIndent is exported by gin/json package. MarshalIndent = json.MarshalIndent // NewDecoder is exported by gin/json package. NewDecoder = json.NewDecoder // NewEncoder is exported by gin/json package. NewEncoder = json.NewEncoder )
同時咱們看到還支持了 jsoniter
segmentfault
// github.com/gin-gonic/gin@v1.6.3/internal/json/jsoniter.go // +build jsoniter package json import "github.com/json-iterator/go" var ( json = jsoniter.ConfigCompatibleWithStandardLibrary // Marshal is exported by gin/json package. Marshal = json.Marshal // Unmarshal is exported by gin/json package. Unmarshal = json.Unmarshal // MarshalIndent is exported by gin/json package. MarshalIndent = json.MarshalIndent // NewDecoder is exported by gin/json package. NewDecoder = json.NewDecoder // NewEncoder is exported by gin/json package. NewEncoder = json.NewEncoder )
那咱們怎麼才能使用到 jsoniter
呢?源碼中已經明確了編譯tag:數組
// +build jsoniter
因此,咱們只需在編譯時帶上這個 tag 就能夠了,例如:框架
go build -tags=jsoniter main.go // 或者 go run -tags=jsoniter main.go
Gin 框架支持的 jsoniter
是默認配置 jsoniter.ConfigCompatibleWithStandardLibrary
。當咱們須要其餘配置或添加一些自定義擴展(好比時間處理)時,就難受了。因而咱們就要本身動手了~post
翻開源碼,咱們能看到 binding.JSON
其實使用的是 jsonBinding{}
這個結構體:ui
// github.com/gin-gonic/gin@v1.6.3/binding/binding.go:73 // These implement the Binding interface and can be used to bind the data // present in the request to struct instances. var ( JSON = jsonBinding{} // 其餘省略了... )
翻開 jsonBinding
源碼看看:.net
// github.com/gin-gonic/gin@v1.6.3/binding/json.go type jsonBinding struct{} func (jsonBinding) Name() string { return "json" } func (jsonBinding) Bind(req *http.Request, obj interface{}) error { if req == nil || req.Body == nil { return fmt.Errorf("invalid request") } return decodeJSON(req.Body, obj) } func (jsonBinding) BindBody(body []byte, obj interface{}) error { return decodeJSON(bytes.NewReader(body), obj) }
發現實現了 BindingBody
這個接口:插件
// github.com/gin-gonic/gin@v1.6.3/binding/binding.go:36 // Binding describes the interface which needs to be implemented for binding the // data present in the request such as JSON request body, query parameters or // the form POST. type Binding interface { Name() string Bind(*http.Request, interface{}) error } // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, // but it reads the body from supplied bytes instead of req.Body. type BindingBody interface { Binding BindBody([]byte, interface{}) error }
那接下來就簡單了,咱們只要實現了這個接口便可,例如:
package custom import ( "bytes" "fmt" "io" "net/http" jsoniter "github.com/json-iterator/go" "github.com/gin-gonic/gin/binding" ) // BindingJSON 替換Gin默認的binding,支持更豐富JSON功能 var BindingJSON = jsonBinding{} // 能夠自定義jsoniter配置或者添加插件 var json = jsoniter.ConfigCompatibleWithStandardLibrary type jsonBinding struct{} func (jsonBinding) Name() string { return "json" } func (jsonBinding) Bind(req *http.Request, obj interface{}) error { if req == nil || req.Body == nil { return fmt.Errorf("invalid request") } return decodeJSON(req.Body, obj) } func (jsonBinding) BindBody(body []byte, obj interface{}) error { return decodeJSON(bytes.NewReader(body), obj) } func decodeJSON(r io.Reader, obj interface{}) error { decoder := json.NewDecoder(r) if binding.EnableDecoderUseNumber { decoder.UseNumber() } if binding.EnableDecoderDisallowUnknownFields { decoder.DisallowUnknownFields() } if err := decoder.Decode(obj); err != nil { return err } return validate(obj) } func validate(obj interface{}) error { if binding.Validator == nil { return nil } return binding.Validator.ValidateStruct(obj) }
自定義 jsonBinding
已經寫好了,可以使用有2種方式:
// binding.JSON 替換成自定義的 ctx.ShouldBindWith(¶ms, binding.JSON) ctx.ShouldBindBodyWith(¶ms, binding.JSON)
上述自定義的方式,還能夠用於其餘包,不只限於 iterator
。從這個方面體現出了 Gin 框架良好的接口設計👍
感謝您的閱讀,以爲內容不錯,點個贊吧 😆
原文地址: https://shockerli.net/post/gin-binding-jsoniter/