请求处理 #
一、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(¶ms); 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