Gin 框架綁定 JSON 參數使用 jsoniter
Gin 框架中,處理 JSON 格式的參數綁定時,默認採用的標準包 encoding/json,然而標準包不能滿足我們的一些要求,比如兼容字符串整型、PHP 空數組、時間格式等。
Gin 框架中,處理 JSON 格式的參數綁定時,默認採用的標準包 encoding/json
,然而標準包不能滿足我們的一些要求,比如兼容字符串整型、PHP 空數組、時間格式等。
最簡單的方式
開發 API 時,需要用到 ShouldBindJSON 綁定傳入的參數到結構體:
// 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
包:
// 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
// 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:
所以,我們只需在編譯時帶上這個 tag 就可以了,例如:
go build -tags=jsoniter main.go
// 或者
go run -tags=jsoniter main.go
自定義的方式
Gin 框架支持的 jsoniter
是默認配置 jsoniter.ConfigCompatibleWithStandardLibrary
。當我們需要其他配置或添加一些自定義擴展(比如時間處理)時,就難受了。於是我們就要自己動手了~
翻開源碼,我們能看到 binding.JSON
其實使用的是 jsonBinding{}
這個結構體:
// 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
源碼看看:
// 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 框架良好的接口設計👍。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/JH3P5r6uGFx7EQTX_DEaIA