自定义响应

除了 JSON、XML 这些标准格式,Gin 还提供了更底层的响应方式,让你可以完全控制响应内容。

返回纯文本

最简单的文本响应:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    
    r.GET("/text", func(c *gin.Context) {
        c.String(http.StatusOK, "这是一段纯文本")
    })
    
    r.GET("/text/format", func(c *gin.Context) {
        name := "张三"
        age := 25
        c.String(http.StatusOK, "姓名:%s,年龄:%d", name, age)
    })
    
    r.Run(":8080")
}

返回字节数据

c.Data() 可以返回任意字节流:

r.GET("/binary", func(c *gin.Context) {
    data := []byte{0x89, 0x50, 0x4E, 0x47}
    c.Data(http.StatusOK, "application/octet-stream", data)
})

从 Reader 读取响应

c.DataFromReader() 适合流式传输:

r.GET("/stream", func(c *gin.Context) {
    file, err := os.Open("large-file.bin")
    if err != nil {
        c.String(http.StatusInternalServerError, "文件打开失败")
        return
    }
    defer file.Close()
    
    info, _ := file.Stat()
    
    c.DataFromReader(
        http.StatusOK,
        info.Size(),
        "application/octet-stream",
        file,
        map[string]string{
            "Content-Disposition": "attachment; filename=large-file.bin",
        },
    )
})

直接操作 ResponseWriter

Gin 允许直接操作底层的 http.ResponseWriter

r.GET("/writer", func(c *gin.Context) {
    c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
    c.Writer.Header().Set("X-Custom-Header", "自定义值")
    c.Writer.WriteHeader(http.StatusOK)
    
    c.Writer.Write([]byte("直接写入响应\n"))
    c.Writer.Write([]byte("第二行内容"))
})

SSE 服务器推送

Server-Sent Events 是一种服务器主动推送的技术:

r.GET("/events", func(c *gin.Context) {
    c.Header("Content-Type", "text/event-stream")
    c.Header("Cache-Control", "no-cache")
    c.Header("Connection", "keep-alive")
    
    for i := 0; i < 10; i++ {
        data := fmt.Sprintf("data: 消息 %d\n\n", i)
        c.Writer.Write([]byte(data))
        c.Writer.Flush()
        time.Sleep(time.Second)
    }
})

客户端使用 EventSource 接收:

const source = new EventSource('/events');
source.onmessage = function(event) {
    console.log(event.data);
};

流式 JSON 响应

大量数据时可以流式返回 JSON:

r.GET("/stream/json", func(c *gin.Context) {
    c.Header("Content-Type", "application/json")
    c.Writer.Write([]byte("["))
    
    first := true
    for i := 0; i < 100; i++ {
        if !first {
            c.Writer.Write([]byte(","))
        }
        first = false
        
        item := fmt.Sprintf(`{"id":%d,"name":"用户%d"}`, i, i)
        c.Writer.Write([]byte(item))
        c.Writer.Flush()
        time.Sleep(100 * time.Millisecond)
    }
    
    c.Writer.Write([]byte("]"))
})

自定义 Content-Type

返回特殊格式的内容:

r.GET("/csv", func(c *gin.Context) {
    c.Header("Content-Type", "text/csv; charset=utf-8")
    c.Header("Content-Disposition", "attachment; filename=data.csv")
    
    csv := "姓名,年龄,城市\n张三,25,北京\n李四,30,上海"
    c.String(http.StatusOK, csv)
})

r.GET("/rss", func(c *gin.Context) {
    c.Header("Content-Type", "application/rss+xml; charset=utf-8")
    
    rss := `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>我的博客</title>
        <link>https://example.com</link>
        <item>
            <title>文章标题</title>
            <description>文章摘要</description>
        </item>
    </channel>
</rss>`
    
    c.String(http.StatusOK, rss)
})

响应重定向

跳转到其他地址:

r.GET("/redirect", func(c *gin.Context) {
    c.Redirect(http.StatusFound, "/target")
})

r.GET("/redirect/external", func(c *gin.Context) {
    c.Redirect(http.StatusFound, "https://example.com")
})

条件响应

根据请求头返回不同内容:

r.GET("/conditional", func(c *gin.Context) {
    accept := c.GetHeader("Accept")
    
    switch {
    case strings.Contains(accept, "application/json"):
        c.JSON(http.StatusOK, gin.H{"message": "JSON 响应"})
    case strings.Contains(accept, "application/xml"):
        c.XML(http.StatusOK, gin.H{"message": "XML 响应"})
    case strings.Contains(accept, "text/html"):
        c.HTML(http.StatusOK, "index.html", gin.H{"message": "HTML 响应"})
    default:
        c.String(http.StatusOK, "文本响应")
    }
})

响应状态码

常用的状态码常量:

r.GET("/status", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "成功"})
})

r.GET("/created", func(c *gin.Context) {
    c.JSON(http.StatusCreated, gin.H{"id": 1, "message": "资源已创建"})
})

r.GET("/no-content", func(c *gin.Context) {
    c.Status(http.StatusNoContent)
})

r.GET("/error", func(c *gin.Context) {
    c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
})

r.GET("/not-found", func(c *gin.Context) {
    c.JSON(http.StatusNotFound, gin.H{"error": "资源不存在"})
})

r.GET("/server-error", func(c *gin.Context) {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
})

小结

Gin 提供了从高层封装到底层 Writer 的完整响应控制能力。日常开发用 c.JSON()c.String() 就够了,特殊场景可以操作 Writer 实现流式响应、SSE 等功能。选择合适的响应方式,能让 API 更加灵活易用。