请求处理 #

一、Context概述 #

1.1 什么是Context #

Context是Gin的核心结构,封装了HTTP请求和响应的所有信息:

go
type Context struct {
    Request   *http.Request
    Writer    ResponseWriter
    // ... 其他字段
}

1.2 Context生命周期 #

text
请求到达 → 创建Context → 中间件链 → 处理函数 → 响应返回 → Context销毁

1.3 Context常用方法 #

方法 说明
Query 获取查询参数
Param 获取路径参数
PostForm 获取表单参数
GetHeader 获取请求头
BindJSON 绑定JSON
JSON 返回JSON响应

二、获取请求参数 #

2.1 查询参数 #

go
func main() {
    r := gin.Default()
    
    r.GET("/search", func(c *gin.Context) {
        // 获取单个参数
        keyword := c.Query("keyword")
        
        // 带默认值
        page := c.DefaultQuery("page", "1")
        size := c.DefaultQuery("size", "10")
        
        // 获取多值参数
        tags := c.QueryArray("tag")
        
        // 获取参数map
        params := c.Request.URL.Query()
        
        c.JSON(200, gin.H{
            "keyword": keyword,
            "page":    page,
            "size":    size,
            "tags":    tags,
            "params":  params,
        })
    })
    
    r.Run()
}

2.2 路径参数 #

go
func main() {
    r := gin.Default()
    
    // 单个参数
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.String(200, "User ID: %s", id)
    })
    
    // 多个参数
    r.GET("/users/:userId/posts/:postId", func(c *gin.Context) {
        userId := c.Param("userId")
        postId := c.Param("postId")
        c.JSON(200, gin.H{
            "userId": userId,
            "postId": postId,
        })
    })
    
    // 通配符
    r.GET("/files/*filepath", func(c *gin.Context) {
        filepath := c.Param("filepath")
        c.String(200, "File: %s", filepath)
    })
    
    r.Run()
}

2.3 表单参数 #

go
func main() {
    r := gin.Default()
    
    r.POST("/login", func(c *gin.Context) {
        // 获取单个参数
        username := c.PostForm("username")
        password := c.PostForm("password")
        
        // 带默认值
        remember := c.DefaultPostForm("remember", "false")
        
        // 获取多值参数
        hobbies := c.PostFormArray("hobby")
        
        c.JSON(200, gin.H{
            "username": username,
            "password": password,
            "remember": remember,
            "hobbies":  hobbies,
        })
    })
    
    r.Run()
}

2.4 绑定到结构体 #

go
type SearchParams struct {
    Keyword string `form:"keyword" binding:"required"`
    Page    int    `form:"page" binding:"min=1"`
    Size    int    `form:"size" binding:"min=1,max=100"`
}

func main() {
    r := gin.Default()
    
    r.GET("/search", func(c *gin.Context) {
        var params SearchParams
        if err := c.ShouldBindQuery(&params); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, params)
    })
    
    r.Run()
}

三、获取请求头 #

3.1 获取单个请求头 #

go
func main() {
    r := gin.Default()
    
    r.GET("/headers", func(c *gin.Context) {
        // 获取单个请求头
        contentType := c.GetHeader("Content-Type")
        authorization := c.GetHeader("Authorization")
        userAgent := c.GetHeader("User-Agent")
        
        c.JSON(200, gin.H{
            "Content-Type":  contentType,
            "Authorization": authorization,
            "User-Agent":    userAgent,
        })
    })
    
    r.Run()
}

3.2 获取所有请求头 #

go
func main() {
    r := gin.Default()
    
    r.GET("/all-headers", func(c *gin.Context) {
        headers := make(map[string]string)
        
        for key, values := range c.Request.Header {
            headers[key] = strings.Join(values, ", ")
        }
        
        c.JSON(200, headers)
    })
    
    r.Run()
}

3.3 常用请求头 #

go
func main() {
    r := gin.Default()
    
    r.GET("/info", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "ContentType":   c.ContentType(),
            "UserAgent":     c.UserAgent(),
            "ContentLength": c.ContentLength,
            "Host":          c.Request.Host,
            "RemoteAddr":    c.Request.RemoteAddr,
            "Method":        c.Request.Method,
            "URL":           c.Request.URL.String(),
            "Protocol":      c.Request.Proto,
        })
    })
    
    r.Run()
}

四、获取请求体 #

4.1 JSON请求体 #

go
type User struct {
    Name  string `json:"name" binding:"required"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"gte=0,lte=150"`
}

func main() {
    r := gin.Default()
    
    r.POST("/users", func(c *gin.Context) {
        var user User
        
        // 绑定JSON
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    r.Run()
}

4.2 XML请求体 #

go
type User struct {
    Name  string `xml:"name"`
    Email string `xml:"email"`
}

func main() {
    r := gin.Default()
    
    r.POST("/xml", func(c *gin.Context) {
        var user User
        
        if err := c.ShouldBindXML(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    r.Run()
}

4.3 原始请求体 #

go
func main() {
    r := gin.Default()
    
    r.POST("/raw", func(c *gin.Context) {
        // 读取原始请求体
        body, err := io.ReadAll(c.Request.Body)
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.Data(200, "text/plain", body)
    })
    
    r.Run()
}

4.4 GetRawData #

go
func main() {
    r := gin.Default()
    
    r.POST("/raw-data", func(c *gin.Context) {
        data, err := c.GetRawData()
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.Data(200, "application/octet-stream", data)
    })
    
    r.Run()
}

五、文件上传 #

5.1 单文件上传 #

go
func main() {
    r := gin.Default()
    
    r.POST("/upload", func(c *gin.Context) {
        // 获取文件
        file, err := c.FormFile("file")
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        // 文件信息
        c.JSON(200, gin.H{
            "filename": file.Filename,
            "size":     file.Size,
            "header":   file.Header,
        })
        
        // 保存文件
        dst := "./uploads/" + file.Filename
        if err := c.SaveUploadedFile(file, dst); err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
    })
    
    r.Run()
}

5.2 多文件上传 #

go
func main() {
    r := gin.Default()
    
    r.POST("/uploads", func(c *gin.Context) {
        form, err := c.MultipartForm()
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        files := form.File["files"]
        var results []gin.H
        
        for _, file := range files {
            dst := "./uploads/" + file.Filename
            if err := c.SaveUploadedFile(file, dst); err != nil {
                results = append(results, gin.H{
                    "filename": file.Filename,
                    "error":    err.Error(),
                })
                continue
            }
            
            results = append(results, gin.H{
                "filename": file.Filename,
                "size":     file.Size,
                "status":   "success",
            })
        }
        
        c.JSON(200, gin.H{"files": results})
    })
    
    r.Run()
}

5.3 文件类型验证 #

go
func main() {
    r := gin.Default()
    
    r.POST("/upload/image", func(c *gin.Context) {
        file, err := c.FormFile("image")
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        // 验证文件类型
        allowedTypes := map[string]bool{
            "image/jpeg": true,
            "image/png":  true,
            "image/gif":  true,
        }
        
        // 打开文件检测类型
        f, _ := file.Open()
        defer f.Close()
        
        buffer := make([]byte, 512)
        f.Read(buffer)
        contentType := http.DetectContentType(buffer)
        
        if !allowedTypes[contentType] {
            c.JSON(400, gin.H{"error": "不支持的文件类型"})
            return
        }
        
        // 验证文件大小 (5MB)
        if file.Size > 5*1024*1024 {
            c.JSON(400, gin.H{"error": "文件大小不能超过5MB"})
            return
        }
        
        dst := "./uploads/" + file.Filename
        c.SaveUploadedFile(file, dst)
        
        c.JSON(200, gin.H{
            "filename":    file.Filename,
            "contentType": contentType,
            "size":        file.Size,
        })
    })
    
    r.Run()
}

六、客户端信息 #

6.1 获取客户端IP #

go
func main() {
    r := gin.Default()
    
    r.GET("/ip", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "ClientIP":  c.ClientIP(),
            "RemoteAddr": c.Request.RemoteAddr,
            "X-Forwarded-For": c.GetHeader("X-Forwarded-For"),
            "X-Real-IP": c.GetHeader("X-Real-IP"),
        })
    })
    
    r.Run()
}

6.2 获取请求方法 #

go
func main() {
    r := gin.Default()
    
    r.Any("/method", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "method": c.Request.Method,
            "isGet":  c.Request.Method == "GET",
            "isPost": c.Request.Method == "POST",
        })
    })
    
    r.Run()
}

6.3 获取URL信息 #

go
func main() {
    r := gin.Default()
    
    r.GET("/url-info", func(c *gin.Context) {
        url := c.Request.URL
        
        c.JSON(200, gin.H{
            "scheme":   url.Scheme,
            "host":     url.Host,
            "path":     url.Path,
            "rawQuery": url.RawQuery,
            "fragment": url.Fragment,
            "fullURL":  url.String(),
        })
    })
    
    r.Run()
}

七、请求上下文 #

7.1 设置和获取值 #

go
func main() {
    r := gin.Default()
    
    r.Use(func(c *gin.Context) {
        // 设置值
        c.Set("userId", "123")
        c.Set("role", "admin")
        c.Next()
    })
    
    r.GET("/context", func(c *gin.Context) {
        // 获取值
        userId, exists := c.Get("userId")
        role, _ := c.Get("role")
        
        // 类型安全的获取
        userIdStr := c.GetString("userId")
        roleStr := c.GetString("role")
        
        c.JSON(200, gin.H{
            "userId":  userId,
            "exists":  exists,
            "role":    role,
            "userIdStr": userIdStr,
            "roleStr": roleStr,
        })
    })
    
    r.Run()
}

7.2 MustGet #

go
func main() {
    r := gin.Default()
    
    r.GET("/must-get", func(c *gin.Context) {
        // MustGet: 如果不存在会panic
        defer func() {
            if err := recover(); err != nil {
                c.JSON(500, gin.H{"error": "key not found"})
            }
        }()
        
        userId := c.MustGet("userId")
        c.JSON(200, gin.H{"userId": userId})
    })
    
    r.Run()
}

八、请求绑定总结 #

8.1 绑定方法对比 #

方法 说明 失败处理
Bind 自动绑定 自动响应400
ShouldBind 自动绑定 返回error
BindJSON 绑定JSON 自动响应400
ShouldBindJSON 绑定JSON 返回error
BindXML 绑定XML 自动响应400
ShouldBindXML 绑定XML 返回error
BindQuery 绑定查询参数 自动响应400
ShouldBindQuery 绑定查询参数 返回error
BindUri 绑定路径参数 自动响应400
ShouldBindUri 绑定路径参数 返回error

8.2 绑定优先级 #

text
ShouldBind 自动检测顺序:
1. Content-Type: application/json → JSON绑定
2. Content-Type: application/xml → XML绑定
3. Content-Type: multipart/form-data → 表单绑定
4. 其他 → URL查询参数绑定

九、总结 #

9.1 核心要点 #

要点 说明
Context 请求上下文核心
查询参数 c.Query()
路径参数 c.Param()
表单参数 c.PostForm()
请求头 c.GetHeader()
JSON绑定 c.ShouldBindJSON()

9.2 最佳实践 #

实践 说明
使用结构体绑定 类型安全,自动验证
使用ShouldBind 灵活处理错误
验证输入 确保数据有效性
错误处理 统一错误响应

9.3 下一步 #

现在你已经掌握了请求处理,接下来让我们学习 响应处理,了解如何返回各种类型的响应!

最后更新:2026-03-28