I want to write an api that will be sent gzipped json data with POST. While the below can deal with simple json in the body, it does not deal if the json is gzipped.
Do we need to explicitly handle the decompression before using c.ShouldBindJSON
?
How to reproduce
package main
import (
"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/postgzip
hello
Expectations
$ echo '{"data" : "hello"}' | gzip | curl -v -i -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @- localhost:8080/postgzip
hello
Actual result
$ 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"}
Environment
- go version:
go version go1.17.2 darwin/amd64
- gin version (or commit ref):
v1.7.4
- operating system: MacOS Monterey
CodePudding user response:
Do we need to explicitly handle the decompression before using c.ShouldBindJSON ?
Of course. Gin ShouldBindJSON
knows nothing of how your payload may or may not be encoded. It expects JSON input, as the method name suggests.
If you wish to write reusable code, you can implement the Binding
interface.
A very minimal example:
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)
}
And then usable with c.ShouldBindWith
, which allows using an arbitrary binding engine:
err := c.ShouldBindWith(&postData, &GzipJSONBinding{})