【问题标题】:Change Content-Type header in Gin middleware更改 Gin 中间件中的 Content-Type 标头
【发布时间】:2022-05-03 06:53:34
【问题描述】:

我设置了一个自定义 gin 中间件来处理错误,但是它确实更改为 Content-Type 标头。

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()})
                }
            }
        }
    }
}

我在响应 Content-Type 标头中得到了一个 text/plain; charset=utf-8

【问题讨论】:

  • 应用程序没有在类型切换的gin.ErrorTypePublic分支中设置内容类型。
  • @thwd 我正在测试gin.ErrorTypeBind 的情况,我明确设置了Content-Type 标头
  • 对于 e.Type == gin.ErrorTypePublic 和 c.Writer.Written 为 true 的情况,你应该有回应吗?
  • @ChidiWilliams TBH,我不确定

标签: go go-gin


【解决方案1】:

将此代码c.Header("Content-Type", "application/json")作为函数体func(c *gin.Context) {...}的第一行

问题是这个包 gin 甚至在你调用 c.JSON 函数之前就在内部改变了头部。这就是问题所在。

【讨论】:

    【解决方案2】:

    问题是调用像c.AbortWithStatus(或c.BindJSON)这样的方法会导致实际写入标题,因为它们在后台调用c.Writer.WriteHeaderNow()。之后您无法覆盖标题。

    解决方法很简单:

    • 不要使用c.BindJSON,调用c.ShouldBindJSON然后手动处理绑定错误
    • 请勿使用c.AbortWithError,请致电c.Status(http.StatusBadRequest)c.Error(err)c.Abort()(以任何顺序)

    您可以为此创建一个包装器:

    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
    }
    

    【讨论】:

      猜你喜欢
      • 2015-05-08
      • 2017-05-19
      • 1970-01-01
      • 2012-07-11
      • 2013-08-22
      • 2012-07-04
      • 2021-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多