You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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),可以基于这个规则处理;如果后续有非数字参数,也可以扩展判断逻辑。

方法思路

  1. 把路径按/分割成片段;
  2. 遍历片段,判断当前片段是否是路径参数(这里假设数字为参数);
  3. 遇到参数片段时,停止拼接,返回前面的路径;如果没有参数,返回原路径。

修改后的代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:35:20