golang gin gonic content-type not setting to application/json with c.JSON

huangapple go评论85阅读模式
英文:

golang gin gonic content-type not setting to application/json with c.JSON

问题

根据官方文档gin-gonic的c.JSON应该将响应头设置为application/json,但是当我从Postman调用我的API时,响应头被设置为text/plain; charset=utf-8

我不明白我漏掉了什么,有什么想法吗?

文档:
> func JSON

> JSON将给定的结构体序列化为JSON并写入响应体中。它还将Content-Type设置为"application/json"。

以下是我的代码示例:

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

编辑

经过调查,log.Println(c.Writer.Header().Get("Content-Type"))没有打印任何内容,显示content-type为空,正如它应该是的。

func writeContentType(w http.ResponseWriter, value []string) {
	header := w.Header()
	log.Println(header.Get("Content-Type")) // <=========== 什么都没有发生
	if val := header["Content-Type"]; len(val) == 0 {
		header["Content-Type"] = value
	}
}

我真的不想在架构中的每个路由中都添加c.Writer.Header().Set("Content-Type", "application/json")

编辑2

似乎binding:"required"破坏了Content-Type头。

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

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(&amp;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 + &quot; is &quot; + 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(&quot;Content-Type&quot;)) // &lt;=========== Nothing happen
	if val := header[&quot;Content-Type&quot;]; len(val) == 0 {
		header[&quot;Content-Type&quot;] = value
	}
}

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

EDIT 2

It seems like binding:&quot;required&quot; break the Content-Type Header

type Credentials struct {
	Email         string        `json:&quot;email&quot; binding:&quot;required&quot;`
	Password      string        `json:&quot;password&quot; binding:&quot;required&quot;`
}

答案1

得分: 6

如果你希望所有的请求都是 JSON 格式的,可以添加一个中间件。

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

在你的路由器上添加以下代码:

router.Use(JSONMiddleware())

这样就可以在每个请求中设置响应的 Content-Type 为 application/json。

英文:

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(&quot;Content-Type&quot;, &quot;application/json&quot;)
        c.Next()
    }
}

On your router add

router.Use(JSONMiddleware())

答案2

得分: 6

使用c.ShouldBindJSON(&credentials)代替c.BindJSON

Gin README.md - 模型绑定和验证

这些方法在内部使用了MustBindWith。如果存在绑定错误,请求将被中止,并使用c.AbortWithError(400, err).SetType(ErrorTypeBind)设置响应状态码为400,Content-Type头部设置为text/plain; charset=utf-8

正如Eutychus在评论中所说:c.BindJSON在内部使用了MustBindWith。如果存在绑定错误,请求将被中止,响应状态码将被设置为400,Content-Type头部将被设置为text/plain; charset=utf-8

ShouldBindJSON在内部使用了ShouldBindWith。如果存在绑定错误,将返回错误,开发者需要适当处理请求和错误。因此,在这种情况下,它是更好的选择。

英文:

Use c.ShouldBindJSON(&amp;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.

As Eutychus said in a comment: c.BindJSON uses MustBindWith under the hood. If there is a binding error, the request is aborted and the response status code is set to 400 and the Content-Type header is set to text/plain; charset=utf-8.

The ShouldBindJSON uses ShouldBindWith under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. That is why it is the better one in this case.

答案3

得分: 4

在查看源代码后,发现如果已经设置了Content-Type头,则不会写入该头。

c.JSON调用此函数,该函数调用以下代码:

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

因此,您的Content-Type必须在其他地方设置。

英文:

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[&quot;Content-Type&quot;]; len(val) == 0 {
		header[&quot;Content-Type&quot;] = value
	}
}

Therefore your Content-Type must be set somewhere else.

答案4

得分: 0

这也可能发生在你的输出实际上不是有效的 JSON 并且无法进行编组(marshal)的情况下。我曾经看到 content-type 被设置为 application/text,当我没有在错误处理程序中返回时,我意外地将一个字符串与 JSON 连接在一起,这是由于我糟糕的错误处理导致的结果。

英文:

This can also happen if your output is not actually valid JSON and fails marshal. I saw content-type being set to application/text when i was not returning in an error handler, and i was accidentally concatenating a string onto JSON as a result of my poor error handling.

huangapple
  • 本文由 发表于 2016年12月13日 04:33:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/41109065.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定