路由参数 #

一、参数类型概述 #

1.1 参数分类 #

类型 来源 获取方法
路径参数 URL路径 c.Param()
查询参数 URL查询字符串 c.Query()
表单参数 请求体 c.PostForm()
JSON参数 请求体JSON c.BindJSON()
文件参数 multipart表单 c.FormFile()

1.2 参数位置示意 #

text
GET /users/123?page=1&size=10
         ↑         ↑
     路径参数    查询参数

POST /users
Content-Type: application/json
{"name": "Alice", "age": 25}
         ↑
      JSON参数

二、路径参数 #

2.1 基本用法 #

go
func main() {
    r := gin.Default()
    
    // 单个路径参数
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.String(200, "用户ID: %s", id)
    })
    
    r.Run()
}

2.2 多个路径参数 #

go
func main() {
    r := gin.Default()
    
    // 多个路径参数
    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.Run()
}

2.3 可选路径参数 #

go
func main() {
    r := gin.Default()
    
    // *filepath 匹配所有后续路径
    r.GET("/files/*filepath", func(c *gin.Context) {
        filepath := c.Param("filepath")
        c.String(200, "文件路径: %s", filepath)
    })
    
    // /files/           → filepath = "/"
    // /files/a.txt      → filepath = "/a.txt"
    // /files/dir/a.txt  → filepath = "/dir/a.txt"
    
    r.Run()
}

2.4 参数验证 #

go
func main() {
    r := gin.Default()
    
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        
        // 验证参数
        if id == "" {
            c.JSON(400, gin.H{"error": "ID不能为空"})
            return
        }
        
        // 验证是否为数字
        if _, err := strconv.Atoi(id); err != nil {
            c.JSON(400, gin.H{"error": "ID必须是数字"})
            return
        }
        
        c.JSON(200, gin.H{"id": id})
    })
    
    r.Run()
}

三、查询参数 #

3.1 获取单个参数 #

go
func main() {
    r := gin.Default()
    
    r.GET("/search", func(c *gin.Context) {
        // 获取查询参数
        keyword := c.Query("keyword")
        
        c.String(200, "搜索关键词: %s", keyword)
    })
    
    // 访问: /search?keyword=gin
    // 输出: 搜索关键词: gin
    
    r.Run()
}

3.2 带默认值的参数 #

go
func main() {
    r := gin.Default()
    
    r.GET("/users", func(c *gin.Context) {
        // 获取参数,如果不存在则使用默认值
        page := c.DefaultQuery("page", "1")
        size := c.DefaultQuery("size", "10")
        
        c.JSON(200, gin.H{
            "page": page,
            "size": size,
        })
    })
    
    // 访问: /users
    // 输出: {"page": "1", "size": "10"}
    
    // 访问: /users?page=2&size=20
    // 输出: {"page": "2", "size": "20"}
    
    r.Run()
}

3.3 获取所有查询参数 #

go
func main() {
    r := gin.Default()
    
    r.GET("/query", func(c *gin.Context) {
        // 获取所有查询参数
        values := c.Request.URL.Query()
        
        result := make(map[string]string)
        for key, val := range values {
            result[key] = val[0]
        }
        
        c.JSON(200, result)
    })
    
    // 访问: /query?name=gin&version=1.9
    // 输出: {"name": "gin", "version": "1.9"}
    
    r.Run()
}

3.4 多值参数 #

go
func main() {
    r := gin.Default()
    
    r.GET("/filter", func(c *gin.Context) {
        // 获取多值参数
        tags := c.QueryArray("tag")
        
        c.JSON(200, gin.H{
            "tags": tags,
        })
    })
    
    // 访问: /filter?tag=go&tag=gin&tag=web
    // 输出: {"tags": ["go", "gin", "web"]}
    
    r.Run()
}

3.5 参数绑定到结构体 #

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()
}

四、表单参数 #

4.1 获取单个表单参数 #

go
func main() {
    r := gin.Default()
    
    r.POST("/login", func(c *gin.Context) {
        username := c.PostForm("username")
        password := c.PostForm("password")
        
        c.JSON(200, gin.H{
            "username": username,
            "password": password,
        })
    })
    
    r.Run()
}

4.2 带默认值的表单参数 #

go
func main() {
    r := gin.Default()
    
    r.POST("/form", func(c *gin.Context) {
        name := c.DefaultPostForm("name", "Guest")
        email := c.DefaultPostForm("email", "")
        
        c.JSON(200, gin.H{
            "name":  name,
            "email": email,
        })
    })
    
    r.Run()
}

4.3 获取所有表单参数 #

go
func main() {
    r := gin.Default()
    
    r.POST("/form", func(c *gin.Context) {
        // 获取所有表单参数
        form, err := c.MultipartForm()
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        values := form.Value
        c.JSON(200, values)
    })
    
    r.Run()
}

4.4 表单绑定到结构体 #

go
type LoginForm struct {
    Username string `form:"username" binding:"required"`
    Password string `form:"password" binding:"required,min=6"`
}

func main() {
    r := gin.Default()
    
    r.POST("/login", func(c *gin.Context) {
        var form LoginForm
        if err := c.ShouldBind(&form); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, gin.H{
            "username": form.Username,
            "message":  "登录成功",
        })
    })
    
    r.Run()
}

五、JSON参数 #

5.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
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    r.Run()
}

5.2 绑定到Map #

go
func main() {
    r := gin.Default()
    
    r.POST("/json", func(c *gin.Context) {
        var data map[string]interface{}
        if err := c.BindJSON(&data); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, data)
    })
    
    r.Run()
}

5.3 获取原始JSON #

go
func main() {
    r := gin.Default()
    
    r.POST("/raw", func(c *gin.Context) {
        // 读取原始JSON数据
        data, err := c.GetRawData()
        if err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.Data(200, "application/json", data)
    })
    
    r.Run()
}

六、URI参数绑定 #

6.1 绑定路径参数 #

go
type UserParams struct {
    ID uint `uri:"id" binding:"required"`
}

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

6.2 绑定多个路径参数 #

go
type PostParams struct {
    UserID uint `uri:"userId" binding:"required"`
    PostID uint `uri:"postId" binding:"required"`
}

func main() {
    r := gin.Default()
    
    r.GET("/users/:userId/posts/:postId", func(c *gin.Context) {
        var params PostParams
        if err := c.ShouldBindUri(&params); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, params)
    })
    
    r.Run()
}

七、参数绑定总结 #

7.1 ShouldBind vs MustBind #

go
func main() {
    r := gin.Default()
    
    // ShouldBind: 绑定失败返回error,不自动响应
    r.POST("/should", func(c *gin.Context) {
        var user User
        if err := c.ShouldBind(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        c.JSON(200, user)
    })
    
    // MustBind: 绑定失败自动响应400,不返回error
    r.POST("/must", func(c *gin.Context) {
        var user User
        if c.Bind(&user) == nil {
            c.JSON(200, user)
        }
    })
    
    r.Run()
}

7.2 绑定方法对比 #

方法 说明 失败处理
ShouldBind 根据Content-Type自动绑定 返回error
ShouldBindJSON 绑定JSON 返回error
ShouldBindXML 绑定XML 返回error
ShouldBindQuery 绑定查询参数 返回error
ShouldBindUri 绑定路径参数 返回error
Bind 自动绑定 自动响应400

7.3 绑定优先级 #

text
ShouldBind 绑定优先级:
1. URL查询参数
2. 表单参数
3. JSON/XML等请求体

八、参数验证 #

8.1 内置验证规则 #

go
type User struct {
    Name     string `binding:"required"`
    Email    string `binding:"required,email"`
    Password string `binding:"required,min=6,max=20"`
    Age      int    `binding:"gte=0,lte=150"`
    Phone    string `binding:"omitempty,len=11"`
    URL      string `binding:"omitempty,url"`
}

8.2 常用验证规则 #

规则 说明
required 必填
min=n 最小值/长度
max=n 最大值/长度
gte=n 大于等于
lte=n 小于等于
email 邮箱格式
url URL格式
len=n 长度等于n
oneof=a b c 值为a、b、c之一
omitempty 可选字段

8.3 自定义验证错误消息 #

go
func main() {
    r := gin.Default()
    
    r.POST("/users", func(c *gin.Context) {
        var user User
        if err := c.ShouldBind(&user); err != nil {
            // 解析验证错误
            var verr validator.ValidationErrors
            if errors.As(err, &verr) {
                errors := make(map[string]string)
                for _, fe := range verr {
                    switch fe.Field() {
                    case "Name":
                        errors["name"] = "名称不能为空"
                    case "Email":
                        errors["email"] = "邮箱格式不正确"
                    case "Password":
                        errors["password"] = "密码长度必须在6-20之间"
                    }
                }
                c.JSON(400, gin.H{"errors": errors})
                return
            }
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    r.Run()
}

九、总结 #

9.1 核心要点 #

要点 说明
路径参数 c.Param(“key”)
查询参数 c.Query(“key”)
表单参数 c.PostForm(“key”)
JSON参数 c.ShouldBindJSON(&struct)
参数验证 binding标签

9.2 最佳实践 #

实践 说明
使用结构体绑定 类型安全,自动验证
使用ShouldBind 更灵活的错误处理
添加验证规则 确保数据有效性
统一错误响应 提升用户体验

9.3 下一步 #

现在你已经掌握了路由参数,接下来让我们学习 路由分组,组织更复杂的路由结构!

最后更新:2026-03-28