'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.ShouldBindJSONand 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 |
