go的http.client默认自动跟随重定向,最终响应的resp.request.url即为重定向后的最终url;若需完整跳转链,须在每次do后记录resp.request.url和statuscode,并通过checkredirect控制流程。

怎么拿到重定向后的最终 URL
Go 的 http.Get 或 http.Client.Do 默认会自动跟随跳转,返回的是最终响应,但原始请求的跳转路径被隐藏了。你不需要自己解析 Location 头或手动循环发请求——resp.Request.URL 就是最终抵达的地址。
常见错误现象:用 req.URL.String() 打印初始 URL,误以为那是结果;或者在 CheckRedirect 里存中间 URL 却忘了并发安全。
-
resp.Request是最终成功请求的 *http.Request,它的URL字段就是终点,直接用resp.Request.URL.String() - 如果请求失败(比如超时或网络断开),
resp可能为nil,必须先判空再取Request - 这个值不包含重定向链中其他跳转 URL,只反映最后一跳的目标
如何记录完整重定向链(含每跳 URL 和状态码)
标准库没提供“一次调用返回所有跳转”的 API,但 Client.CheckRedirect 是唯一官方支持的钩子入口。它不给状态码,所以得把状态码和跳转目标“对齐”:在主流程里每次收到 *http.Response 就记下 resp.StatusCode 和 resp.Request.URL,再让 CheckRedirect 控制是否继续。
常见错误现象:CheckRedirect 函数里写 return nil,直接 panic;或者没传 context.WithTimeout,导致重定向卡死。
- 定义一个切片(如
[]struct{ URL string; Code int }),在每次Do后追加当前resp.Request.URL和resp.StatusCode -
CheckRedirect只负责决策:“这跳要不要跟?” 返回http.ErrUseLastResponse表示停在这里,返回其他 error(如自定义)表示拒绝跳转 - 别在
CheckRedirect里尝试读resp—— 它还没生成,参数只有*http.Request和[]*http.Request历史
为什么设置了 Client.Timeout 还会卡住
Client.Timeout 看似简单,但它只控制“从调用 Do 开始到收到最终响应”的总时间,**不包括 DNS 解析、TLS 握手、连接建立等环节的超时**。更关键的是:如果你自定义了 Transport 但没正确实现 RoundTripper 接口(比如漏掉 CancelRequest,虽然已弃用,但旧代码或兼容逻辑仍可能依赖),就会触发 "doesn't support CancelRequest" 错误。
常见错误现象:请求卡在 DNS 查询或 TLS handshake,Timeout 完全不生效;或用了自定义 RoundTripper 后编译报错。
- 优先用
context.WithTimeout包裹整个Do调用,它是端到端真正可控的超时方式 -
http.Transport自带Proxy、IdleConnTimeout、TLSHandshakeTimeout等字段,按需设置比依赖Client.Timeout更精准 - 不要自己实现
RoundTripper,除非真有不可绕过的需求;复用http.DefaultTransport并配置其字段更安全
怎样安全禁用自动跳转并手动处理
禁用自动跳转不是为了“不用重定向”,而是为了掌控每一步:比如只允许同域名跳转、过滤付费墙链接、或在跳转前检查 Cookie 是否已设置。核心是把 Client.CheckRedirect 设为返回 http.ErrUseLastResponse 的函数。
常见错误现象:禁用后发起 POST 请求,跳转时 body 消失、Header(如 User-Agent)没继承、或重复使用已关闭的 io.ReadCloser 导致 panic。
- 返回
http.ErrUseLastResponse后,resp就是 301/302 响应本身,可读resp.Header.Get("Location")获取下一跳 - 手动构造新请求时,记得复制原始请求的
Header(除Host外),并根据状态码决定是否保留Body:307/308 要原样重发,301/302 应丢弃 body 改用 GET - 如果原始请求有
Body,确保它是可重读的(例如用bytes.NewReader包装字节切片,而非直接传os.Stdin)
重定向链的真实复杂度不在“跳几次”,而在于每跳的协议一致性(HTTP/1.1 vs HTTP/2)、代理规则匹配、Cookie 域限制、以及 TLS 证书校验是否跨跳生效——这些细节不会出现在 CheckRedirect 回调里,只能靠 transport 层配置和 context 超时兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











