导读:本期聚焦于李修然创作的《Ruby Faraday重定向历史如何追踪?FollowRedirects中间件详解》,敬请观看详情。为什么用Faraday发请求后拿不到完整的重定向链路?很多接口调试场景下,我们需要知道请求到底经历了哪些302跳转、每一跳的状态码和Location是什么。本文围绕Faraday的FollowRedirects中间件展开,介绍如何开启自动重定向跟随、如何通过env对象读取重定向历史、如何利用on_callback钩子记录每一跳的请求与响应详情,并对比手动解析响应与自动跟随两种方案的差异。同时给出在代理、Cookie保持、循环重定向防护等常见场景下的实践建议与完整代码示例,帮助你彻底掌握Faraday中重定向链路的追踪方法。

Falda...(更正)Faraday 是 Ruby 生态中最流行的 HTTP 客户端库之一,它以中间件架构著称。当我们在 Faraday 中开启自动重定向跟随后,默认情况下你只能拿到最终响应,中间经历了多少次 302、每一跳请求打到了哪个 URL,这些信息似乎“消失”了。其实 Faraday 提供了完整的机制来追踪重定向历史,本文将围绕 FollowRedirects 中间件展开详细讲解。

Ruby Faraday重定向历史如何追踪?FollowRedirects中间件详解

一、FollowRedirects 中间件的基本原理

Faraday 的 FaradayMiddleware::FollowRedirects(新版本中已并入 Faraday 官方,命名空间为 Faraday::Middleware 体系下的 follow_redirects)是处理 3xx 响应的核心中间件。它的工作流程是:请求发出后,如果响应状态码是 301、302、303、307 或 308,中间件会解析响应头中的 Location 字段,得到下一个目标地址,然后自动发起新的请求,直到拿到非 3xx 响应或者达到最大跳转次数上限。

理解这一点的关键在于中间件的执行顺序。Faraday 的中间件分为请求中间件、响应中间件和适配器,FollowRedirects 属于响应处理链路中的一环,它包裹着底层适配器。也就是说,每一次“重新发起请求”,实际上都是中间件内部再次调用了一次完整的请求栈,这就为记录历史提供了天然的切入点。

默认配置下,中间件的最大跳转次数为 3,可以通过 limit 选项调整。如果超过限制会抛出 FaradayMiddleware::RedirectLimitReached 异常,这是防止无限循环重定向(例如 A 跳到 B、B 又跳回 A)的保护机制。除了次数限制,中间件内部还有一个已访问 URL 集合,用于检测循环重定向。

二、开启自动重定向并读取重定向历史

最基础的用法是在连接上启用 follow_redirects 中间件:

require 'faraday'
require 'faraday/follow_redirects'

conn = Faraday.new(url: 'https://ipipp.com') do |f|
  f.request :url_encoded
  f.response :follow_redirects, limit: 5
  f.adapter :net_http
end

response = conn.get('/some/redirect/path')

puts response.status   # 最终响应的状态码
puts response.env.url  # 最终落地的 URL

请求完成后,response.env 中保存了这次请求的完整环境信息。其中 response.env.url 是最终实际请求的地址,而不是你最初传入的地址,这一点在做下载链接解析时特别有用——很多下载站会给一个临时跳转链,最终的真实文件地址就藏在 env.url 里。

不过 env 本身只保留最后一跳的信息。如果你想拿到完整的跳转链条,需要借助 on_callback 钩子,这是追踪重定向历史最正统的方式:

redirect_history = []

conn = Faraday.new(url: 'https://ipipp.com') do |f|
  f.response :follow_redirects, limit: 5,
    on_callback: lambda do |old_env, new_env|
      redirect_history << {
        from: old_env.url.to_s,
        status: old_env.status,
        location: old_env.response_headers['location'],
        to: new_env.url.to_s
      }
    end
  f.adapter :net_http
end

response = conn.get('/redirect')

redirect_history.each_with_index do |hop, i|
  puts "第#{i + 1}跳: #{hop[:from]} --#{hop[:status]}--> #{hop[:to]}"
end

on_callback 会在每一次决定跟随重定向时被调用,参数是旧的 env 和即将发起的新请求 env。通过它可以拿到每一跳的来源地址、状态码、Location 头以及目标地址,等于把整个重定向链完整地“录制”了下来。这个能力在调试第三方登录回调、支付回调链路时非常实用。

三、Cookie 保持与循环重定向防护

重定向场景有一个高频坑点:很多服务在 302 时会通过 Set-Cookie 下发会话标识,如果跟随重定向的过程中 Cookie 丢了,最终响应可能变成登录页。FollowRedirects 中间件默认会保留请求头,配合 cookie 中间件即可实现整个跳转链路的会话保持:

conn = Faraday.new(url: 'https://ipipp.com') do |f|
  f.request :url_encoded
  f.request :authorization, 'Bearer', token
  f.response :follow_redirects, limit: 5
  f.response :logger                  # 打印每一次请求,方便观察跳转过程
  f.adapter :net_http
end

加上 :logger 中间件后,控制台会输出每一次 HTTP 交互的明细,即使不写 on_callback,也能直观看到中间每一跳的请求地址与响应状态,适合快速排查问题。需要注意 logger 中间件应放在 follow_redirects 之后声明,这样它能观察到中间件内部的重复调用过程。

关于循环重定向,中间件的防护分两层:一是 limit 次数上限,二是内部对相同 URL 的重复检测。如果你在业务中确实存在合法的多次跳转(比如 SSO 单点登录动辄五六跳),记得把 limit 调大,例如 limit: 10。同时建议用 on_callback 记录历史,一旦出现循环,你能立刻从日志中定位到是哪两个地址在互相踢皮球。

另外一个细节是 303 与 307、308 的语义差异:303 会把后续请求方法转为 GET 并丢弃请求体,而 307、308 保持原方法和原请求体。中间件已经正确处理了这些差异,但如果你手动解析重定向,就必须自己实现这套规则,这也是推荐直接使用 FollowRedirects 而非手写循环的原因之一。

四、手动跟随与自动跟随的方案对比

有些场景下你不希望自动跟随,而是想自己控制每一跳,比如需要在某一跳插入逻辑判断,或者只跟随同域名重定向以防开放重定向漏洞。此时可以不开中间件,手动循环处理:

url = 'https://ipipp.com/start'
history = []

5.times do
  resp = Faraday.get(url)
  history << { url: url, status: resp.status,
              location: resp.headers['location'] }
  break unless [301, 302, 303, 307, 308].include?(resp.status)
  break unless resp.headers['location']
  url = URI.join(url, resp.headers['location']).to_s
end

手动方案的优势是控制粒度细,可以在每一步校验目标域名白名单、记录耗时、甚至中途终止;缺点是要自己处理 Location 相对地址解析(用 URI.join)、请求方法转换等细节,代码量明显更多。自动跟随方案则胜在简洁可靠,配合 on_callback 也能拿到完整历史,适合绝大多数常规场景。

综合来看,如果你只是需要“拿到最终结果并知道中间跳了哪些站”,用 FollowRedirects 加 on_callback 是最省事且信息完整的组合;如果安全要求高、跳转策略复杂,则手动循环更可控。把这两套方案放进工具箱,按需取用,重定向链路对你来说就再无黑盒。

RubyFaradayFollowRedirects修改时间:2026-09-01 20:44:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。