为了账号安全,请及时绑定邮箱和手机立即绑定

在绑定之前显式处理压缩的 json

在绑定之前显式处理压缩的 json

Go
慕斯709654 2022-10-10 19:22:41
我想编写一个 api,它将通过 POST 发送 gzip 压缩的 json 数据。虽然下面可以处理正文中的简单 json,但如果 json 被压缩,则不处理。使用前是否需要明确处理解压c.ShouldBindJSON?如何重现package mainimport (    "github.com/gin-gonic/gin"    "log"    "net/http")func main() {    r := gin.Default()    r.POST("/postgzip", func(c *gin.Context) {                type PostData struct {            Data string `binding:"required" json:"data"`        }                var postdata PostData        if err := c.ShouldBindJSON(&postdata); err != nil {            log.Println("Error parsing request body", "error", err)            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})            return        }        log.Printf("%s", postdata)        if !c.IsAborted() {            c.String(200, postdata.Data)        }    })    r.Run()}❯ echo '{"data" : "hello"}' | curl -X POST -H "Content-Type: application/json" -d @- localhost:8080/postgziphello期望$ echo '{"data" : "hello"}' | gzip | curl -v -i -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @- localhost:8080/postgziphello实际结果$ echo '{"data" : "hello"}' | gzip | curl -v -i -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @- localhost:8080/postgzip{"error":"invalid character '\\x1f' looking for beginning of value"}环境去版本:go version go1.17.2 darwin/amd64gin 版本(或提交参考):v1.7.4操作系统:MacOS Monterey
查看完整描述

2 回答

?
慕容森

TA贡献1853条经验 获得超18个赞

我们是否需要在使用 c.ShouldBindJSON 之前显式处理解压缩?

当然。GinShouldBindJSON不知道您的有效负载可能会或可能不会被编码。正如方法名称所暗示的那样,它需要 JSON 输入。

如果你想编写可重用的代码,你可以实现这个Binding接口。

一个非常小的例子:

type GzipJSONBinding struct {

}


func (b *GzipJSONBinding) Name() string {

    return "gzipjson"

}


func (b *GzipJSONBinding) Bind(req *http.Request, dst interface{}) error {

    r, err := gzip.NewReader(req.Body)

    if err != nil {

        return err

    }

    raw, err := io.ReadAll(r)

    if err != nil {

        return err

    }

    return json.Unmarshal(raw, dst)

}

然后可用于c.ShouldBindWith,它允许使用任意绑定引擎:


err := c.ShouldBindWith(&postData, &GzipJSONBinding{})


查看完整回答
反对 回复 2022-10-10
?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

打开的完整工作示例Content-Encoding

curl 尝试使用它


$ echo '{"data" : "hello"}' | gzip | curl -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @- localhost:8080/json

hello

$ curl -X POST -H "Content-Type: application/json" --data-raw '{"data" : "hello"}' localhost:8080/json

hello

package main


import (

    "bytes"

    "compress/gzip"

    "encoding/json"

    "fmt"

    "github.com/gin-gonic/gin"

    "github.com/gin-gonic/gin/binding"

    "io"

    "log"

    "net/http"

)


type PostData struct {

    Data string `binding:"required" json:"data"`

}


func main() {

    r := gin.Default()

    r.POST("/json", func(c *gin.Context) {


        var postdata PostData


        contentEncodingHeader := c.GetHeader("Content-Encoding")

        switch contentEncodingHeader {

        case "gzip":

            if err := c.ShouldBindBodyWith(&postdata, gzipJSONBinding{}); err != nil {

                log.Println("Error parsing GZIP JSON request body", "error", err)

                c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})

                return

            }

        case "":

            if err := c.ShouldBindJSON(&postdata); err != nil {

                log.Println("Error parsing JSON request body", "error", err)

                c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})

                return

            }

        default:

            log.Println("unsupported Content-Encoding")

            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "unsupported Content-Encoding"})

            return


        }


        log.Printf("%s", postdata)

        if !c.IsAborted() {

            c.String(200, postdata.Data)

        }

    })

    r.Run()

}


type gzipJSONBinding struct{}


func (gzipJSONBinding) Name() string {

    return "gzipjson"

}


func (gzipJSONBinding) Bind(req *http.Request, obj interface{}) error {

    if req == nil || req.Body == nil {

        return fmt.Errorf("invalid request")

    }

    r, err := gzip.NewReader(req.Body)

    if err != nil {

        return err

    }

    raw, err := io.ReadAll(r)

    if err != nil {

        return err

    }

    return json.Unmarshal(raw, obj)

}


func (gzipJSONBinding) BindBody(body []byte, obj interface{}) error {

    r, err := gzip.NewReader(bytes.NewReader(body))

    if err != nil {

        return err

    }

    return decodeJSON(r, obj)

}


func decodeJSON(r io.Reader, obj interface{}) error {

    decoder := json.NewDecoder(r)


    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)

}


查看完整回答
反对 回复 2022-10-10
  • 2 回答
  • 0 关注
  • 161 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号