英文:
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(&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"`
}
答案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("Content-Type", "application/json")
c.Next()
}
}
On your router add
router.Use(JSONMiddleware())
答案2
得分: 6
使用c.ShouldBindJSON(&credentials)
代替c.BindJSON
。
这些方法在内部使用了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(&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["Content-Type"]; len(val) == 0 {
header["Content-Type"] = 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论