现在的 API 开发,JSON 已经是事实上的标准数据交换格式了。Gin 返回 JSON 响应非常简单,而且提供了好几种方式来满足不同的需求。
最常用的就是 c.JSON() 方法,传入状态码和数据就行:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"name": "张三",
"age": 25,
"city": "北京",
})
})
r.GET("/users", func(c *gin.Context) {
users := []gin.H{
{"name": "张三", "age": 25},
{"name": "李四", "age": 30},
}
c.JSON(http.StatusOK, users)
})
r.Run(":8080")
}
gin.H 是一个很方便的快捷方式,本质上是 map[string]interface{},用来快速构建 JSON 对象。
实际项目中,我们更多时候会用结构体来定义响应数据:
type UserResponse struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsAdmin bool `json:"is_admin"`
}
type ApiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
r.GET("/profile", func(c *gin.Context) {
user := UserResponse{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
IsAdmin: false,
}
c.JSON(http.StatusOK, ApiResponse{
Code: 0,
Message: "success",
Data: user,
})
})
结构体的好处是类型安全,而且可以通过 json tag 控制字段名和序列化行为。
调试的时候,格式化的 JSON 更容易阅读。Gin 提供了 IndentedJSON 方法:
r.GET("/pretty", func(c *gin.Context) {
c.IndentedJSON(http.StatusOK, gin.H{
"message": "这是格式化的 JSON",
"data": gin.H{
"items": []string{"a", "b", "c"},
},
})
})
不过要注意,生产环境不建议用这个,因为会增加响应体积。
有时候我们需要防止 JSON 劫持,可以用 SecureJSON:
r.GET("/secure", func(c *gin.Context) {
c.SecureJSON(http.StatusOK, []gin.H{
{"name": "张三"},
{"name": "李四"},
})
})
这会在响应前面加上 while(1);,防止恶意网站通过 script 标签获取你的 JSON 数据。
如果你的 API 需要支持跨域的 JSONP 请求:
r.GET("/jsonp", func(c *gin.Context) {
c.JSONP(http.StatusOK, gin.H{
"message": "JSONP 响应",
})
})
访问 /jsonp?callback=myCallback,会返回 myCallback({"message":"JSONP 响应"})。
如果需要确保 JSON 中的非 ASCII 字符被转义:
r.GET("/ascii", func(c *gin.Context) {
c.AsciiJSON(http.StatusOK, gin.H{
"name": "张三",
"city": "北京",
})
})
返回的结果会是 {"name":"\u5f20\u4e09","city":"\u5317\u4eac"}。
默认情况下,Go 的 JSON 序列化会把 <、>、& 这些 HTML 特殊字符转义。如果你不想转义:
r.GET("/pure", func(c *gin.Context) {
c.PureJSON(http.StatusOK, gin.H{
"html": "<b>这是纯文本 HTML</b>",
})
})
这样 < 和 > 就不会被转义成 \u003c 和 \u003e 了。
Gin 提供了丰富的 JSON 响应方法,日常开发中 c.JSON() 基本够用。遇到特殊场景,比如调试、安全防护、跨域支持,可以选择对应的方法。记住,生产环境尽量用紧凑的 JSON 格式,减少网络传输量。