Gin 框架绑定 JSON 参数使用 jsoniter

共 4905字,需浏览 10分钟

 ·

2021-09-18 13:53

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 !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)

同时我们看到还支持了 jsoniter

// github.com/gin-gonic/gin@v1.6.3/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 呢?源码中已经明确了编译 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.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)}

发现实现了 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 customimport (    "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.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 已经写好了,可使用有 2 种方式:

// binding.JSON 替换成自定义的ctx.ShouldBindWithms, binding.JSON)ctx.ShouldBindBodyWithms, binding.JSON)

上述自定义的方式,还可以用于其他包,不仅限于 iterator。从这个方面体现出了 Gin 框架良好的接口设计👍。


转自公众号:




推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 33
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报