bigywater.tech
Gin · 15

FullPath():路由模板怎么拿到

给接口打监控的时候,用 c.Request.URL.Path 当标签,每个用户 ID 都会变成一个新时间序列。c.FullPath() 返回的是路由模板 /users/:id,正好能避免这个问题。这篇验证它在哪些情况下有值、哪些情况下是空串。

16000 → 3
按 URL.Path 与按 FullPath 打监控标签得到的不同值个数
""
404 请求里 c.FullPath() 的值
5 / 6
含一个重定向请求时,全局中间件被执行的次数 / 请求数

FullPath 从哪来

实测 · 本机执行输出
  context.go 的 FullPath(Gin v1.12.0):
     177 | func (c *Context) FullPath() string {
     178 |     return c.fullPath
     179 | }
  context.go: 109 |     c.fullPath = ""
  gin.go: 721 |             c.fullPath = value.fullPath

FullPath() 本身只是返回字段。它的值在请求开头被清空,查到路由后由路由节点上存着的 fullPath(注册时的完整模板,包含 Group 前缀)赋值,因此在所有中间件和 handler 里都可以取到——前提是路由已经匹配。

什么时候有值

实测 · 本机执行输出
    GET /users/42              中间件里 Next 之前 FullPath="/users/:id"                 之后 FullPath="/users/:id" 状态=200
    GET /files/a/b.txt         中间件里 Next 之前 FullPath="/files/*path"               之后 FullPath="/files/*path" 状态=200
    GET /api/v1/orders/7/items/9 中间件里 Next 之前 FullPath="/api/v1/orders/:oid/items/:iid" 之后 FullPath="/api/v1/orders/:oid/items/:iid" 状态=200
    GET /nope                  中间件里 Next 之前 FullPath=""                           之后 FullPath="" 状态=404
    GET /dir                   重定向 301 → /dir/(没有经过中间件)
    GET /users/                中间件里 Next 之前 FullPath=""                           之后 FullPath="" 状态=404
    共 6 个请求,中间件执行了 5 次

匹配到路由时,不管是参数路由、通配路由还是 Group 里的路由,返回的都是带 :oid、*path 的模板。404 时是空串。被重定向的请求(尾斜杠 301)在进入全局中间件之前就返回了,所以中间件根本拿不到它,监控里看不到这类请求。

监控标签

实测 · 本机执行输出
    按 URL.Path 打标签:16000 个不同的值
    按 FullPath 打标签:3 个不同的值:map[(未匹配):1000 /users/:id:10000 /users/:id/orders/:oid:5000]

标签的基数决定监控系统的开销:按 URL.Path 打标签,一万个用户就是一万个时间序列;按 FullPath 只有几条。404 时 FullPath 是空串,标签要换成一个固定值(比如“(未匹配)”);如果退回去用原始 URL.Path 兜底,扫描器乱扫的路径又会把基数撑爆。

Routes() 的 Handler

实测 · 本机执行输出
    GET    /users/:id     handler=main.getUser
    GET    /api/list      handler=main.S.List-fm
    POST   /users         handler=main.main.func1
    PUT    /api/x         handler=github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1

r.Routes() 返回每条路由的方法、路径和 handler 名。名字来自链上最后一个函数,所以中间件不会出现在里面;如果一条路由的最后一个 handler 本身是中间件(比如上面的 gin.Recovery()),显示的就是中间件的闭包名。方法值带 -fm 后缀。

分步演示

FullPath
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Go 1.27.1(brew)/ Gin v1.12.0(go.mod 里 require,源码在模块缓存里);用 go run / go test 直接编译运行,需要库时经代理从 proxy.golang.org 下载;HTTP 实验用 httptest 或本机回环端口(9700 以上);计时用 time.Now / testing.B,数字每次运行会有小幅抖动。请求用 httptest 直接调用 ServeHTTP;监控标签实验用 10000 个用户 ID 的请求(每个偶数 ID 再发一个订单路径请求,每 10 个再发一个扫描器路径),标签用 map 计数;每种情形各一次
    • 没有验证:真实的 Prometheus 中间件(如 ginprometheus)怎么处理 FullPath;HandlerName 在内联或泛型函数上的显示;与 UseRawPath 同时使用时 FullPath 是否改变;参数名带特殊字符时的显示
    京ICP备2026067502号-1京公网安备11010502063487号