响应渲染
Gin 内置支持以多种格式渲染响应,包括 JSON、XML、YAML 和 ProtoBuf。这使得构建支持内容协商的 API 变得简单直接。
渲染方法总览
| 方法 | 输出格式 | Content-Type | 适用场景 |
|---|---|---|---|
c.JSON(code, obj) |
JSON | application/json |
REST API(最常用) |
c.IndentedJSON(code, obj) |
格式化 JSON | application/json |
开发/调试 |
c.PureJSON(code, obj) |
JSON(不转义 HTML) | application/json |
返回 HTML 片段 |
c.SecureJSON(code, obj) |
JSON(防劫持前缀) | application/json |
JSONP 防护 |
c.AsciiJSON(code, obj) |
ASCII-JSON | application/json |
非 ASCII 字符转义 |
c.XML(code, obj) |
XML | application/xml |
遗留系统/SOAP |
c.YAML(code, obj) |
YAML | application/yaml |
配置类接口 |
c.ProtoBuf(code, obj) |
Protocol Buffers | application/protobuf |
高性能微服务间通信 |
c.String(code, str) |
纯文本 | text/plain |
简单文本响应 |
c.HTML(code, name, obj) |
HTML 模板 | text/html |
服务端渲染 |
c.Data(code, contentType, data) |
自定义 | 自定义 | 任意二进制数据 |
c.File(filepath) |
文件 | 推断 | 静态文件下载 |
c.Stream(step, contentType, reader) |
流式 | 自定义 | 大文件传输 |
JSON 渲染
基本 JSON
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello",
"count": 42,
})
})
// → {"message":"hello","count":42}格式化 JSON(开发环境)
r.GET("/pretty", func(c *gin.Context) {
c.IndentedJSON(200, gin.H{
"nested": gin.H{
"key": "value",
},
})
})
// {
// "nested": {
// "key": "value"
// }
// }结构体序列化
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"` // 空值不序列化
Roles []string `json:"roles,omitempty"`
}
r.GET("/user", func(c *gin.Context) {
c.JSON(200, User{
Name: "willow",
Age: 25,
})
})
// → {"name":"willow","age":25}SecureJSON — JSON 劫持防护
c.SecureJSON() 在 JSON 前添加 while(1); 前缀,防止 JSON 劫持攻击:
r.GET("/secure", func(c *gin.Context) {
c.SecureJSON(200, gin.H{"data": "sensitive"})
})
// → while(1);{"data":"sensitive"}🔬 深入原理:JSON 劫持依赖于浏览器允许通过
<script>标签跨域加载 JSON 并通过__defineSetter__或Object.prototype重定义来窃取数据。while(1);前缀让作为<script>加载的响应成为一个无限循环,从而阻止数据泄露。
PureJSON — 不转义 HTML
c.JSON() 会将 HTML 特殊字符(<、>、& 等)转义为 Unicode 实体。c.PureJSON() 则保持原样:
r.GET("/pure", func(c *gin.Context) {
c.PureJSON(200, gin.H{"html": "<b>bold</b>"})
})
// → {"html":"<b>bold</b>"}
// 对比普通 JSON
// → {"html":"<b>bold</b>"}AsciiJSON — 非 ASCII 转义
r.GET("/ascii", func(c *gin.Context) {
c.AsciiJSON(200, gin.H{"lang": "中文"})
})
// → {"lang":"中文"}XML 渲染
r.GET("/xml", func(c *gin.Context) {
c.XML(200, gin.H{"message": "hello", "status": 200})
})
// <map>
// <message>hello</message>
// <status>200</status>
// </map>YAML 渲染
r.GET("/yaml", func(c *gin.Context) {
c.YAML(200, gin.H{"status": "ok", "services": []string{"api", "web"}})
})ProtoBuf 渲染
适用于服务间高性能通信:
// 需要 .proto 文件及生成的代码
r.GET("/protobuf", func(c *gin.Context) {
reps := []int64{1, 2, 3}
data := &protoexample.Test{
Label: proto.String("hello"),
Reps: reps,
}
c.ProtoBuf(200, data)
})💡 ProtoBuf 产生更小的载荷和更快的序列化速度,但需要共享 schema 定义(
.proto文件)。
HTML 模板渲染
func main() {
r := gin.Default()
// 加载模板目录
r.LoadHTMLGlob("templates/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(200, "index.tmpl", gin.H{
"title": "Gin Demo",
"items": []string{"Go", "Gin", "Web"},
})
})
}<!-- templates/index.tmpl -->
<html>
<body>
<h1>{{ .title }}</h1>
<ul>
{{ range .items }}
<li>{{ . }}</li>
{{ end }}
</ul>
</body>
</html>🚨 陷阱:Go 的
html/template默认转义 HTML 输出以防止 XSS,这是安全的。如果需要输出原始 HTML,在模板中使用template.HTML类型(但需谨慎)。
文件处理
// 返回单个文件
r.GET("/file", func(c *gin.Context) {
c.File("./downloads/report.pdf")
})
// 返回文件流(支持 Range 请求)
r.GET("/stream", func(c *gin.Context) {
f, _ := os.Open("./large-file.zip")
defer f.Close()
c.DataFromReader(200, -1, "application/zip", f, nil)
})
// 文件附件下载(Content-Disposition: attachment)
r.GET("/download", func(c *gin.Context) {
c.FileAttachment("./downloads/report.pdf", "report-2024.pdf")
})自定义渲染器
Gin 允许注册自定义 Render 实现:
type CustomRender struct {
Data string
}
func (r CustomRender) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
_, err := w.Write([]byte(r.Data))
return err
}
func (r CustomRender) WriteContentType(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/vnd.custom+json")
}
// 使用
r.GET("/custom", func(c *gin.Context) {
c.Render(200, CustomRender{Data: `{"custom": "format"}`})
})渲染选择指南
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| REST API 响应 | c.JSON() |
通用,浏览器友好 |
| 开发调试 | c.IndentedJSON() |
可读性好 |
| 前端已处理 HTML | c.PureJSON() |
不转义 |
| JSONP 接口 | c.SecureJSON() |
防劫持 |
| 微服务间 RPC | c.ProtoBuf() |
高性能、小载荷 |
| 配置文件接口 | c.YAML() |
人类可读 |
| 文件下载 | c.FileAttachment() |
正确设置 Content-Disposition |
| 流式传输 | c.DataFromReader() |
支持 Range,内存友好 |