Gin路由参数仅支持:param和*path两种占位符,不支持正则;需用c.Param()获取后手动校验,因底层httprouter仅做静态与通配匹配。

路由参数必须用 :param 语法,不能写正则
Gin 的路由参数只支持两种占位符::id(命名参数)和 *path(通配符),不解析正则表达式。写 /user/:id/[0-9]+ 或 /user/:id\d+ 会被当作字面路径处理,根本不会触发参数捕获,甚至可能注册失败或匹配不到。
底层 httprouter 只做静态前缀树匹配,没有正则引擎介入。想靠路径定义本身实现格式约束,行不通。
-
:id匹配任意非斜杠字符,直到下一个/或路径结尾,例如/user/123abc、/user/都能匹配 -
*path匹配包括斜杠在内的所有剩余路径,且只能放在末尾,如/file/*filepath - 多个命名参数之间必须用
/显式分隔,/user/:id/:name合法,/user/:id:name不合法
c.Param("name") 是唯一取值方式,空值需手动判断
c.Param("id") 返回的是字符串,不管实际 URL 中对应段是否存在——如果路径是 /user/(末尾带斜杠但没值),c.Param("id") 会返回空字符串 "",不是 nil,也不会报错。
常见错误是直接对空字符串调 strconv.Atoi,导致 panic;或者没检查就进业务逻辑,引发 ID 为 0 的误查。
- 始终先判空:
if idStr := c.Param("id"); idStr == "" { c.AbortWithStatusJSON(400, gin.H{"error": "id required"}) } - 数字校验别在 handler 里反复
regexp.Compile,提前全局复用:var idRegex = regexp.MustCompile(`^d{1,10}$`) - 需要强类型转换时,用
strconv.ParseUint比Atoi更安全(避免负数)
嵌套路由参数和通配符共存要小心顺序
Gin 的基数树按「静态路径 > :param > *path」优先级匹配,同一 method 下,更具体的规则会优先生效。但这个顺序只在同一棵树内起作用,不跨 method。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
比如你注册了:
router.GET("/files/:name", handlerA)
router.GET("/files/*path", handlerB)
那么 GET /files/logo.png 会命中 handlerA,因为 :name 比 *path 优先级高;但 GET /files/css/app.css 会走 handlerB,因为 :name 不匹配含斜杠的路径。
- 不要依赖
*path去兜底所有子路径,它只在没有更具体规则时才生效 -
:param和*path不能混在中间,/api/v1/:version/*rest合法,/api/v1/*rest/:suffix不合法 - 如果想让
/files/logo.png和/files/css/app.css都走同一 handler,统一用*path,再在 handler 内用strings.Split或正则解析
想做路径格式校验,必须在 handler 里手动做
没有“声明式正则路由”这种东西。Gin 不提供类似 Express 的 app.get("/user/:id(\d+)", ...) 语法,也不推荐引入第三方 regex 路由中间件——它们往往绕过基数树,性能差,且与 Gin 的中间件生命周期不兼容。
最稳的方式就是放行所有符合路径结构的请求,然后在 handler 开头集中校验。
- 校验失败立刻
c.Abort(),阻止后续中间件(比如鉴权、日志)执行 - 错误响应用
c.JSON(400, ...),别用c.String,保持 API 格式一致 - 如果校验逻辑重复多处,抽成中间件函数,但注意:中间件无法改变路由匹配结果,只能 abort 或继续
真正容易被忽略的是:路径参数的“捕获”和“校验”是两个阶段,Gin 只负责前者;后者必须由你显式控制,没有捷径,也没有隐式 fallback。


















