'Change Content-Type header in Gin middleware
I have a custom gin middleware set up to handle errors but however, it does change to the Content-Type
header.
package middleware
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
func validationErrorToText(e validator.FieldError) string {
switch e.Tag() {
case "required":
return fmt.Sprintf("%s is required", e.Field())
case "max":
return fmt.Sprintf("%s cannot be longer than %s", e.Field(), e.Param())
case "min":
return fmt.Sprintf("%s must be longer than %s", e.Field(), e.Param())
case "email":
return fmt.Sprintf("Invalid email format")
case "len":
return fmt.Sprintf("%s must be %s characters long", e.Field(), e.Param())
}
return fmt.Sprintf("%s is not valid", e.Field())
}
func Errors() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
// Only run if there are some errors to handle
if len(c.Errors) > 0 {
for _, e := range c.Errors {
// Find out what type of error it is
switch e.Type {
case gin.ErrorTypePublic:
// Only output public errors if nothing has been written yet
if !c.Writer.Written() {
c.JSON(c.Writer.Status(), gin.H{"error": e.Error()})
}
case gin.ErrorTypeBind:
errs := e.Err.(validator.ValidationErrors)
list := make(map[int]string)
for field, err := range errs {
list[field] = validationErrorToText(err)
}
// Make sure we maintain the preset response status
status := http.StatusBadRequest
if c.Writer.Status() != http.StatusOK {
status = c.Writer.Status()
}
c.Header("Content-Type", "application/json")
c.JSON(status, gin.H{
"status": "error",
"errors": list,
})
default:
c.JSON(http.StatusBadRequest, gin.H{"errors": c.Errors.JSON()})
}
}
}
}
}
I get a text/plain; charset=utf-8
in the response Content-Type
header instead.
Solution 1:[1]
Put this code c.Header("Content-Type", "application/json")
as the 1st line in the function body func(c *gin.Context) {...}
The problem is this package gin changes the header somewhere internally even before you call the c.JSON function. That's the issue.
Solution 2:[2]
The problem is that calling methods like c.AbortWithStatus
(or c.BindJSON
) leads to actually writing headers, since under the hood they invoke c.Writer.WriteHeaderNow()
. You cannot overwrite the header afterwards.
The solution is pretty simple:
- do not use
c.BindJSON
, callc.ShouldBindJSON
and then handle binding errors manually - do not use
c.AbortWithError
, callc.Status(http.StatusBadRequest)
,c.Error(err)
,c.Abort()
(in any order)
You can create a wrapper just for that:
func BindJSON(c *gin.Context, obj interface{}) error {
if err := c.ShouldBindJSON(obj); err != nil {
c.Status(http.StatusBadRequest)
c.Error(err)
c.Abort()
return err
}
return nil
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Dharman |
Solution 2 | Iskander Sitdikov |