第05章:路径参数与查询参数
本章目标
学完本章后,你将能够:
- 使用
:param和*param获取路径参数 - 使用
c.Query()/c.DefaultQuery()获取查询参数 - 正确处理数组参数
- 根据场景选择路径参数还是查询参数
前置知识
- 需要先学习:第03-04章(路由基础与分组)
5.1 路径参数(Path Parameter)
路径参数是 URL 路径中的动态部分:
/users/123 → id = 123
/users/abc → id = abc
/articles/hello-world → slug = hello-world基础用法::param
go
// :id 匹配单个路径段
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id") // 获取路径参数
c.JSON(200, gin.H{
"message": fmt.Sprintf("查询用户 %s", id),
})
})bash
curl http://localhost:8080/users/123
# {"message":"查询用户 123"}
curl http://localhost:8080/users/abc
# {"message":"查询用户 abc"}多个路径参数
go
// :owner 和 :repo 是独立的参数
api.GET("/repos/:owner/:repo", func(c *gin.Context) {
owner := c.Param("owner")
repo := c.Param("repo")
c.JSON(200, gin.H{
"owner": owner,
"repo": repo,
})
})bash
curl http://localhost:8080/api/repos/gin-gonic/gin
# {"owner":"gin-gonic","repo":"gin"}通配符 *param:匹配剩余全部路径
go
// *filepath 匹配 /static/ 后面的所有内容
r.GET("/static/*filepath", func(c *gin.Context) {
filepath := c.Param("filepath") // 返回 "/css/style.css"(包含前导斜杠)
c.String(200, "请求的文件:%s", filepath)
})bash
curl http://localhost:8080/static/css/style.css
# 请求的文件:/css/style.css
curl http://localhost:8080/static/js/app.js
# 请求的文件:/js/app.js5.2 查询参数(Query Parameter)
查询参数是 URL 中 ? 后面的 key=value 对:
/users?name=张三&age=25&city=北京
└──┬──┘ └─┬─┘ └──┬──┘
name age city获取查询参数
go
r.GET("/users", func(c *gin.Context) {
// 直接获取(不存在时返回空字符串)
name := c.Query("name")
// 带默认值(不存在时返回默认值)
age := c.DefaultQuery("age", "18")
// 获取并判断是否存在
city, exists := c.GetQuery("city")
if !exists {
city = "未知"
}
c.JSON(200, gin.H{
"name": name,
"age": age,
"city": city,
})
})bash
# 传入所有参数
curl "http://localhost:8080/users?name=张三&age=25&city=北京"
# {"age":"25","city":"北京","name":"张三"}
# 不传 age 和 city
curl "http://localhost:8080/users?name=张三"
# {"age":"18","city":"未知","name":"张三"}获取多个同名参数(数组)
go
r.GET("/users", func(c *gin.Context) {
// 获取所有同名的 ids 参数
ids := c.QueryArray("ids")
// 或者用 map 形式
queryMap := c.QueryMap("filter")
c.JSON(200, gin.H{
"ids": ids,
"filter": queryMap,
})
})bash
curl "http://localhost:8080/users?ids=1&ids=2&ids=3&filter[name]=张三"
# {"filter":{"name":"张三"},"ids":["1","2","3"]}5.3 路径参数 vs 查询参数:怎么选
路径参数 /users/:id | 查询参数 ?name=张三 |
|---|---|
| 用于标识资源 | 用于过滤/排序/搜索 |
| 是 URL 的一部分,必须传 | 是可选的,不传也能访问 |
| RESTful 风格中表示"哪个资源" | RESTful 风格中表示"什么条件" |
✅ 好的设计:
GET /users/1 ← 路径参数:标识"1号用户"这个资源
GET /users?status=active ← 查询参数:筛选活跃用户
GET /users?page=2&page_size=10 ← 查询参数:分页
❌ 不好的设计:
GET /users?id=1 ← 路径参数更适合标识资源
GET /users/active ← /active 是状态不是资源5.4 获取所有查询参数
go
r.GET("/debug", func(c *gin.Context) {
// 获取原始查询字符串
rawQuery := c.Request.URL.RawQuery
fmt.Println("原始查询:", rawQuery)
// 获取解析后的 map
queryMap := c.Request.URL.Query()
for key, values := range queryMap {
fmt.Printf("%s = %v\n", key, values)
}
c.JSON(200, gin.H{"query": rawQuery})
})5.5 完整实战:搜索接口
go
package main
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// GET /api/v1/users?keyword=张&status=active&page=1&page_size=10
r.GET("/api/v1/users", func(c *gin.Context) {
keyword := c.DefaultQuery("keyword", "")
status := c.DefaultQuery("status", "all")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
c.JSON(http.StatusOK, gin.H{
"keyword": keyword,
"status": status,
"page": page,
"page_size": pageSize,
"message": fmt.Sprintf("搜索关键词=%s, 状态=%s, 第%d页, 每页%d条", keyword, status, page, pageSize),
})
})
r.Run(":8080")
}bash
curl "http://localhost:8080/api/v1/users?keyword=张&status=active&page=1&page_size=10"
# {"keyword":"张","message":"搜索关键词=张, 状态=active, 第1页, 每页10条","page":1,"page_size":10,"status":"active"}常见错误
错误1:混淆 : 和 *
go
// :id 只匹配一个路径段(不含 /)
r.GET("/files/:filename") // /files/abc.txt ✅ /files/a/b.txt ❌
// *filepath 匹配所有剩余路径(含 /)
r.GET("/files/*filepath") // /files/a/b.txt ✅错误2:Query 参数类型忘了转换
go
// ❌ c.Query 返回的是 string!
age := c.Query("age") // age 是 "25",不是 25
// 不能直接做数学运算
// ✅ 转换
age, _ := strconv.Atoi(c.DefaultQuery("age", "0"))错误3:查询参数中有中文忘了 URL 编码
bash
# ❌ 可能乱码
curl "http://localhost:8080/users?name=张三"
# ✅ curl 会自动编码,但如果自己拼接 URL 需要编码
# Go 中:url.QueryEscape("张三") → "%E5%BC%A0%E4%B8%89"本章小结
:param匹配单段路径,*param匹配剩余全部路径c.Param("id")获取路径参数c.Query("key")/c.DefaultQuery("key", "default")获取查询参数c.QueryArray("ids")获取数组参数- 路径参数标识资源,查询参数表示条件
- Query 参数都是 string,需要手动类型转换
练习题
- 实现
GET /articles/:id,返回文章 ID 和标题。 - 实现
GET /search?q=关键词&type=article&limit=10,返回搜索条件。 - 实现一个接口同时支持路径参数和查询参数:
GET /users/:id/orders?status=paid。 - 用
*filepath实现一个简单的文件浏览接口。 - (思考题)
GET /users/:id和GET /users?id=1哪个更 RESTful?为什么?