场景还原
假如有如下的接口,它会返回一些数据,我们通过一个Response结构体把响应做一个封装
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Response struct {
code int
data any
msg string
}
func main() {
r := gin.Default()
r.GET("/data", func(c *gin.Context) {
response := Response{
code: http.StatusOK,
data: "some data",
msg: http.StatusText(http.StatusOK),
}
c.JSON(http.StatusOK, response)
})
r.Run(":8080")
}
运行后访问接口,期望的响应是
{
"Code":200,
"Data":"some data",
"Msg":"OK",
}
而实际得到的响应是
显然和我们的预期不符,下面开始排查问题
排查
gin框架在调用c.JSON时,对于我们传进去的data数据,实际上是调用json.Marshal进行处理的
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj any) error {
writeContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj)
if err != nil {
return err
}
_, err = w.Write(jsonBytes)
return err
}
而json.Marshal有一个规则,就是如果解析的对象是一个结构体,那么只有导出字段,也就是首字母大写的字段会被解析
问题迎刃而解,我们的Response里字段都是包级别的,不可导出,也就不会被解析出来,自然在响应里看不到
结语
这个问题比较隐蔽,主要还是对于封装函数的用法不了解,踩过的坑记住,后面不踩了
Author: KreanXie