Context 结构体

Context 是 Gin 最重要的数据结构,理解它的组成对深入使用 Gin 很有帮助。

Context 的定义

Context 结构体的主要字段:

type Context struct {
    Request   *http.Request
    Writer    ResponseWriter
    
    Params   Params
    handlers HandlersChain
    index    int8
    fullPath string
    
    engine *Engine
    
    mu sync.RWMutex
    
    Keys map[string]interface{}
    
    Errors errorMsgs
    
    Accepted []string
}

常用方法分类

请求相关

c.Request.Method
c.Request.URL
c.Request.Header
c.Request.Body

c.FullPath()
c.ClientIP()
c.ContentType()
c.IsWebsocket()
c.UserAgent()

参数获取

c.Param("id")
c.Query("name")
c.DefaultQuery("name", "default")
c.PostForm("field")
c.DefaultPostForm("field", "default")
c.GetRawData()
c.GetHeader("Authorization")
c.GetHeaders()

响应相关

c.JSON(200, data)
c.XML(200, data)
c.String(200, "text")
c.HTML(200, "template.html", data)
c.Data(200, "application/octet-stream", bytes)
c.File("path/to/file")
c.Redirect(302, "/new-url")

数据存储

c.Set("key", value)
c.Get("key")
c.MustGet("key")
c.GetString("key")
c.GetInt("key")
c.GetFloat("key")
c.GetTime("key")
c.GetDuration("key")

响应头操作

c.Header("Content-Type", "application/json")
c.GetHeader("Authorization")
c.SetCookie("name", "value", 3600, "/", "", false, true)
c.Cookie("name")

中间件控制

c.Next()
c.Abort()
c.AbortWithStatus(401)
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
c.IsAborted()

Context 的生命周期

每个请求都会创建一个新的 Context:

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.Writer.reset(w)
    c.Request = req
    c.reset()
    
    engine.handleHTTPRequest(c)
    
    engine.pool.Put(c)
}

Context 使用 sync.Pool 复用,避免频繁创建对象。

获取请求信息示例

package main

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

func main() {
    r := gin.Default()
    
    r.Any("/info", func(c *gin.Context) {
        info := gin.H{
            "method":      c.Request.Method,
            "path":        c.Request.URL.Path,
            "fullPath":    c.FullPath(),
            "clientIP":    c.ClientIP(),
            "contentType": c.ContentType(),
            "userAgent":   c.UserAgent(),
            "headers":     c.Request.Header,
            "query":       c.Request.URL.Query(),
            "isAjax":      c.GetHeader("X-Requested-With") == "XMLHttpRequest",
            "isWebSocket": c.IsWebsocket(),
        }
        
        c.JSON(http.StatusOK, info)
    })
    
    r.Run(":8080")
}

Context 的 Writer

Context 的 Writer 是对 http.ResponseWriter 的封装:

type ResponseWriter interface {
    http.ResponseWriter
    http.Hijacker
    http.Flusher
    http.CloseNotifier
    
    Status() int
    Size() int
    WriteString(string) (int, error)
    Written() bool
    WriteHeaderNow()
    Pusher() http.Pusher
}

常用操作:

c.Writer.Status()
c.Writer.Size()
c.Writer.Header()
c.Writer.Write([]byte("data"))
c.Writer.WriteString("text")
c.Writer.Flush()

获取请求体

r.POST("/body", func(c *gin.Context) {
    body, err := c.GetRawData()
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    c.String(200, "Body: %s", string(body))
})

注意:GetRawData 只能读取一次,如果需要在多处使用,需要重新设置:

body, _ := c.GetRawData()
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))

判断请求类型

func isJSON(c *gin.Context) bool {
    return strings.Contains(c.ContentType(), "application/json")
}

func isFormData(c *gin.Context) bool {
    return strings.Contains(c.ContentType(), "application/x-www-form-urlencoded")
}

func isMultipart(c *gin.Context) bool {
    return strings.Contains(c.ContentType(), "multipart/form-data")
}

小结

Context 是 Gin 的核心,封装了请求和响应的所有操作。理解它的结构和方法分类,有助于快速找到需要的功能。记住 Context 是请求级别的,每个请求独立,生命周期从请求开始到响应结束。