Go net/http自定义RoundTripper提取无路径参数的REST API路径
问题
我正在编写一个自定义RoundTripper,用于收集客户端指标(往返延迟),这个RoundTripper会和HTTP Client Transport配合,用于不同的REST服务。我希望按API方法对延迟指标分组,所以需要提取不含路径参数和查询字符串的REST API路径。
我试过用r.URL.Path,它能去掉查询字符串,但没法截断路径参数。举几个例子:
- API地址为
/data-service/api/v1/streams/2664?start=1645920000000&end=1648512000000时,需要提取的路径是"/data-service/api/v1/streams"; - API地址为
/entities-service/api/v1/entities/1234时,需要提取的路径是"/entities-service/api/v1/entities"; - 无路径参数和查询参数的情况保持原样,比如
"session-service/api/v1/token"。
当前r.URL.Path在第一种情况返回"/data-service/api/v1/streams/2664",第二种情况返回"/entities-service/api/v1/entities/1234"(只去掉了查询字符串),请问怎么同时去除路径参数和查询字符串?
现有代码如下:
type customTransport struct { rtp http.RoundTripper reqStart time.Time reqEnd time.Time } func (tr *customTransport) RoundTrip(r *http.Request) (*http.Response, error) { tr.reqStart = time.Now() resp, err := tr.rtp.RoundTrip(r) tr.reqEnd = time.Now() // Calculate latency and publish it to prometheus latency := tr.reqEnd.Sub(tr.reqStart) // Strip path param and query param here extractedPath := r.URL.Path return resp, err }
解决方案
要实现这个需求,核心是识别路径中的参数部分并截断。根据你的场景,路径参数看起来都是数字(比如2664、1234),可以基于这个规则处理;如果后续有非数字参数,也可以扩展判断逻辑。
方法思路
- 把路径按
/分割成片段; - 遍历片段,判断当前片段是否是路径参数(这里假设数字为参数);
- 遇到参数片段时,停止拼接,返回前面的路径;如果没有参数,返回原路径。
修改后的代码
import ( "strings" "unicode" ) type customTransport struct { rtp http.RoundTripper reqStart time.Time reqEnd time.Time } func (tr *customTransport) RoundTrip(r *http.Request) (*http.Response, error) { tr.reqStart = time.Now() resp, err := tr.rtp.RoundTrip(r) tr.reqEnd = time.Now() // Calculate latency and publish it to prometheus latency := tr.reqEnd.Sub(tr.reqStart) // Strip path param and query param here extractedPath := extractBasePath(r.URL.Path) return resp, err } // extractBasePath 移除路径中的参数部分(假设参数为数字) func extractBasePath(path string) string { segments := strings.Split(path, "/") var baseSegments []string for _, seg := range segments { // 判断当前片段是否是数字类型的路径参数 isParam := true for _, c := range seg { if !unicode.IsDigit(c) { isParam = false break } } if isParam { // 遇到参数片段,停止拼接 break } baseSegments = append(baseSegments, seg) } // 重新拼接路径,处理空片段避免多余的/ result := strings.Join(baseSegments, "/") // 保持原路径的开头/(如果原路径有) if len(path) > 0 && path[0] == '/' && result != "" { return "/" + result } return result }
扩展说明
如果你的路径参数不只是数字(比如字符串ID),可以调整isParam的判断逻辑:
- 如果有预定义的路由模板(比如知道哪些位置是参数),可以提前存储模板,用路径去匹配模板;
- 或者根据服务的API规则,比如参数片段是UUID、特定前缀等,针对性编写判断逻辑。
内容的提问来源于stack exchange,提问作者Abhay
相关产品推荐
相关产品推荐

