Description
c.JSON(code, interface) does not work as intended. Returns the right JSON body but with the wrong content-type header and wrong HTTP status code.
How to reproduce
// handlers/handler.go
func Handler(c *gin.Context) {
var body HandlerDTO
if err := lib.ParseAndValidate(c, &body); err != nil {
log.Info("Code: ", strconv.Itoa(err.Code)) // Outputs the error code 422 with no issue
log.Info(err) // Outputs the error with no issues
c.JSON(err.Code, err) // Responds with the correct json body, but the wrong content type, `Content-Type: text/plain; charset=utf-8` and the error code 400 instead of the one specified (422).
}
}
// ---
// lib/parser.go
func ParseBody(c *gin.Context, body interface{}) *common.ErrorModel {
if err := c.BindJSON(body); err != nil {
return &common.ErrorModel{
ResponseModel: common.ResponseModel{
Code: http.StatusUnprocessableEntity,
Success: false,
},
Error: common.Error{
Code: "json_error",
Message: "there was an error while parsing the body",
},
}
}
return nil
}
func ParseAndValidate(c *gin.Context, body interface{}) *common.ErrorModel {
if err := ParseBody(c, body); err != nil {
return err
}
return Validate(body)
}
// ---
// lib/validator.go
var validate = validator.New()
func Validate(payload interface{}) *common.ErrorModel {
err := validate.Struct(payload)
// error extraction...
if err != nil {
return &common.ErrorModel{
ResponseModel: common.ResponseModel{
Code: http.StatusUnprocessableEntity,
Success: false,
},
Error: common.Error{
Code: "validation_error",
Message: "validator has detected errors",
},
}
}
return nil
}
Expectations
Respond with JSON as the content type, and respond with the correct
Actual result
Correct JSON body but the wrong content-type header and 400 as the HTTP status
Environment
- go version: go1.17.6
- gin version (or commit ref): v1.7.7
- operating system: Ubuntu Desktop 21.10
Comment From: kivox
I've done some more testing, and the issue is only present if the request is interrupted at the ParseBody call in ParseAndValidate. When the error is returned from the Validate the expected output is received, correct content-type and correct HTTP status.
Comment From: kivox
I have found the culprit.
When following the function c.BindJSON(), it shows that it uses calls to c.MustBind(), which then calls to c.ShouldBindWith() if there is an error when calling ShouldBindWith, it tries to abort the request with a BadRequest error, I am unsure as to why it is unsuccessful. Most likely because I am still continuing the request after the error. c.BindJSON() was most likely intended to be used in the handler itself, which I presume would abord the request properly. If I reproduce the error in a handler it does indeed work properly.
Solution: As the c.BindJSON() is a shortcut for c.MustBindWith(). Replacing it with c.ShouldBindJSON() works fine.