0
votes

According to the official documentation, c.JSON of gin-gonic should set the response header to application/json, but when I call my API from Postman, the response header is set to text/plain; charset=utf-8

I don't understand what I am missing, any idea ?

Doc :

func JSON

JSON serializes the given struct as JSON into the response body. It also sets the Content-Type as "application/json".

Here is a sample of my code :

func postLogin(c *gin.Context) {
    var credentials DTO.Credentials
    if err := c.BindJSON(&credentials); err == nil {
        c.JSON(buildResponse(services.CheckUserCredentials(credentials)))
    } else {
        var apiErrors = DTO.ApiErrors{}
        for _, v := range err.(validator.ValidationErrors) {
            apiErrors.Errors = append(apiErrors.Errors, DTO.ApiError{Field: v.Field, Message: v.Field + " is " + v.Tag})
        }
        c.JSON(http.StatusBadRequest, apiErrors)
    }
}

EDIT

After investigation, log.Println(c.Writer.Header().Get("Content-Type")) doesn't print any thing, showing content-type is empty as it should be.

func writeContentType(w http.ResponseWriter, value []string) {
    header := w.Header()
    log.Println(header.Get("Content-Type")) // <=========== Nothing happen
    if val := header["Content-Type"]; len(val) == 0 {
        header["Content-Type"] = value
    }
}

I really don't want to have to add c.Writer.Header().Set("Content-Type", "application/json") to every route in my architecture...

EDIT 2

It seems like binding:"required" break the Content-Type Header

type Credentials struct {
    Email         string        `json:"email" binding:"required"`
    Password      string        `json:"password" binding:"required"`
}
3

3 Answers

3
votes

After looking at the source, it looks like it won't write the Content-Type header if it is already set.

c.JSON calls this function which calls the following code:

func writeContentType(w http.ResponseWriter, value []string) {
    header := w.Header()
    if val := header["Content-Type"]; len(val) == 0 {
        header["Content-Type"] = value
    }
}

Therefore your Content-Type must be set somewhere else.

2
votes

If you expect all your requests to be JSON, add a middle ware instead.

func JSONMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Content-Type", "application/json")
        c.Next()
    }
}

On your router add

router.Use(JSONMiddleware())
-1
votes

Use c.ShouldBindJSON(&credentials) instead of c.BindJSON.

Gin README.md - Model binding and validation

These methods use MustBindWith under the hood. If there is a binding error, the request is aborted with c.AbortWithError(400, err).SetType(ErrorTypeBind). This sets the response status code to 400 and the Content-Type header is set to text/plain; charset=utf-8.