路径参数::name 与 *path 通配

本章解决什么问题:/users/42 这类“同一条路由、动态取值”是 REST 服务的核心需求。本章讲 :name 单段参数、*path 通配参数与 c.Param 取值,以及路由冲突与优先级规则。

冒号参数 :name

用 :name 声明路径中的占位段,它只匹配一个非空路径段(不含 /)。处理函数用 c.Param("name") 取回字符串:

package main

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

func main() {
	r := gin.Default()

	r.GET("/users/:id", func(c *gin.Context) {
		id := c.Param("id")
		c.JSON(200, gin.H{"id": id})
	})
	r.GET("/users/:id/orders/:oid", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"id":  c.Param("id"),
			"oid": c.Param("oid"),
		})
	})
	r.GET("/files/*path", func(c *gin.Context) {
		// *path 会包含开头的斜杠,例如 /files/a/b.txt 得到 "/a/b.txt"
		c.JSON(200, gin.H{"path": c.Param("path")})
	})

	r.Run(":8080")
}

第二段路由演示同一条路径可以有多个冒号参数,只要所处段位不同即可。

通配参数 *path

以 * 开头的段是“抓取剩余一切”的通配符:必须放在路径最后,且匹配结果包含起始的斜杠。典型用途是静态文件、下载、前端路由回退这类“前缀固定、后面任意”的场景。

与 :name 的区别::name 至少要有一个字符,*path 可能匹配到空内容或只有斜杠的片段,取值后通常需要再处理。

取值与类型转换

  • c.Param 永远返回字符串,且只来自 URL 路径本身,与查询串无关;
  • 需要数值时自己用 strconv 转换,或直接做结构体绑定:字段 tag 写 binding:"uri",用 c.ShouldBindUri(&s),路径参数会按名填入字段(绑定体系在第 7 章统一展开)。

冲突与优先级

Gin 的路由是 radix tree,非法注册会在启动期直接 panic,常见情形:

  • /users/new 与 /users/:name:静态段优先级更高,两者可以共存;
  • /users/:name 与 /users/:id:同一位置的参数名不同,冲突 panic;
  • /users/:id 与 /users/:id/orders:参数名一致且后续结构可延伸,可以共存。

一句话规则:同深度下静态节点优先于参数节点,参数节点之间靠“同名 + 不同后继结构”区分。

关键点

  • :name 匹配单个非空段;*path 抓取剩余全部并带前导斜杠,只能出现在末尾。
  • 路径参数与查询参数是两回事:前者在路径里,后者在 ? 后面的键值对里。
  • 路由冲突属于“启动即失败”的保护机制,应在开发期暴露,而不是靠 catch 绕过。

小结

动态路径段让少量路由覆盖大量资源::name 负责单段取值,*path 负责前缀通配。下一章看 URL 中另一类参数——Query 与表单参数。

笔记加载中…