Gin 框架綁定 JSON 參數(shù)使用 jsoniter
Gin 框架中,處理 JSON 格式的參數(shù)綁定時(shí),默認(rèn)采用的標(biāo)準(zhǔn)包 encoding/json,然而標(biāo)準(zhǔn)包不能滿足我們的一些要求,比如兼容字符串整型、PHP 空數(shù)組、時(shí)間格式等。
Gin 框架中,處理 JSON 格式的參數(shù)綁定時(shí),默認(rèn)采用的標(biāo)準(zhǔn)包 encoding/json,然而標(biāo)準(zhǔn)包不能滿足我們的一些要求,比如兼容字符串整型、PHP 空數(shù)組、時(shí)間格式等。
最簡單的方式
開發(fā) API 時(shí),需要用到 ShouldBindJSON 綁定傳入的參數(shù)到結(jié)構(gòu)體:
// github.com/gin-gonic/[email protected]/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 默認(rèn)采用 encoding/json 包:
// github.com/gin-gonic/[email protected]/internal/json/json.go// +build !jsoniterpackage jsonimport "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)
同時(shí)我們看到還支持了 jsoniter
// github.com/gin-gonic/[email protected]/internal/json/jsoniter.go// +build jsoniterpackage jsonimport "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 呢?源碼中已經(jīng)明確了編譯 tag:
所以,我們只需在編譯時(shí)帶上這個(gè) tag 就可以了,例如:
go build -tags=jsoniter main.go// 或者go run -tags=jsoniter main.go
自定義的方式
Gin 框架支持的 jsoniter 是默認(rèn)配置 jsoniter.ConfigCompatibleWithStandardLibrary。當(dāng)我們需要其他配置或添加一些自定義擴(kuò)展(比如時(shí)間處理)時(shí),就難受了。于是我們就要自己動(dòng)手了~
翻開源碼,我們能看到 binding.JSON 其實(shí)使用的是 jsonBinding{} 這個(gè)結(jié)構(gòu)體:
// github.com/gin-gonic/[email protected]/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 源碼看看:
// github.com/gin-gonic/[email protected]/binding/json.gotype 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)}
發(fā)現(xiàn)實(shí)現(xiàn)了 BindingBody 這個(gè)接口:
// github.com/gin-gonic/[email protected]/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() stringBind(*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 {BindingBindBody([]byte, interface{}) error}
那接下來就簡單了,我們只要實(shí)現(xiàn)了這個(gè)接口即可,例如:
package customimport ("bytes""fmt""io""net/http"jsoniter "github.com/json-iterator/go""github.com/gin-gonic/gin/binding")// BindingJSON 替換Gin默認(rèn)的binding,支持更豐富JSON功能var BindingJSON = jsonBinding{}// 可以自定義jsoniter配置或者添加插件var json = jsoniter.ConfigCompatibleWithStandardLibrarytype 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 已經(jīng)寫好了,可使用有 2 種方式:
// binding.JSON 替換成自定義的ctx.ShouldBindWith(?ms, binding.JSON)ctx.ShouldBindBodyWith(?ms, binding.JSON)
上述自定義的方式,還可以用于其他包,不僅限于 iterator。從這個(gè)方面體現(xiàn)出了 Gin 框架良好的接口設(shè)計(jì)??。
轉(zhuǎn)自公眾號(hào):
推薦閱讀
