Ruby Net::HTTP - 跟随301重定向

31

我的用户提交URL(指向Mixcloud上的混音)并且我的应用程序使用它们执行Web请求。

一个好的URL返回200状态码:

uri = URI.parse("http://www.mixcloud.com/ErolAlkan/hard-summer-mix/")
request = Net::HTTP.get_response(uri)(
#<Net::HTTPOK 200 OK readbody=true>

但是如果您忘记了结尾的斜杠,那么我们本来良好的网址将返回301:

uri = "http://www.mixcloud.com/ErolAlkan/hard-summer-mix"
#<Net::HTTPMovedPermanently 301 MOVED PERMANENTLY readbody=true> 

对于404错误也会发生同样的事情:

# bad path returns a 404
"http://www.mixcloud.com/bad/path/" 
# bad path minus trailing slash returns a 301
"http://www.mixcloud.com/bad/path"
  1. 如何“深入”查看301重定向并确定它是否将我们带到有效的资源或错误页面?
  2. 是否有一种工具可以提供关于特定域名可能应用于其URL的规则的全面概述?
5个回答

55

如果你输入的URL不符合Web服务器的预期,那么301重定向是相当常见的。它们发生的频率比你想象的要高得多,只是因为浏览器会自动处理所有这些,所以你通常不会注意到。

有两个替代方案:

1:使用open-uri

open-uri会自动处理重定向。所以你需要做的就是:

require 'open-uri' 
...
response = open('http://xyz...').read

如果您在 HTTP 和 HTTPS 之间重定向时遇到问题,可以查看此处的解决方案:
Ruby open-uri redirect forbidden

2:使用 Net::HTTP 处理重定向

def get_response_with_redirect(uri)
   r = Net::HTTP.get_response(uri)
   if r.code == "301"
     r = Net::HTTP.get_response(URI.parse(r['location']))
   end
   r
end

如果您想要更聪明的话,当您收到404响应时,可以尝试添加或删除URL中缺失的反斜杠。您可以创建一个名为get_response_smart的方法,除了重定向之外,还处理此URL操作。


1
@stephen - 太好了 :) 如果你想更多地了解HTTP状态码,可以直接在这里查看规范:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html - Casper
3
如果你把第四行改成 r = get_response_with_redirect(URI.parse(r.header['location'])),就可以处理多个重定向。 - ReggieB
2
我似乎无法让open-uri遵循重定向。这是在更近期的ruby版本中发生了变化吗?(运行2.2) - dmur
1
@dmur 你是否在HTTP和HTTPS之间进行重定向?如果是这样,请看这里:https://dev59.com/xF4c5IYBdhLWcg3w6Ns7 - Casper
大多数人可能需要的是“response = open('http://xyz ...')。read”。 - DannyB
显示剩余2条评论

8
我无法理解如何对已接受的答案进行评论(这个问题可能已经关闭),但我应该指出r.header现在已经过时,所以r.header['location']应该被替换为r['location'](参见https://dev59.com/TWw05IYBdhLWcg3w72Q0#6934503)。

我刚刚意识到我可以自己编辑已接受的解决方案,所以我刚刚编辑了它 :) - PhilGA

4
rest-client 可以在不需要额外配置的情况下自动重定向 GETHEAD 请求,效果非常好。
  • 对于状态码在 200 到 207 之间的结果,将返回 RestClient::Response
  • 对于状态码为 301、302 或 307 的结果,如果请求是 GET 或 HEAD,则会跟随重定向
  • 对于状态码为 303 的结果,将跟随重定向并将请求转换为 GET

使用示例:

require 'rest-client'

RestClient.get 'http://example.com/resource'

rest-client README 还提供了一个关于如何使用POST请求进行重定向的示例:

begin
  RestClient.post('http://example.com/redirect', 'body')
rescue RestClient::MovedPermanently,
       RestClient::Found,
       RestClient::TemporaryRedirect => err
  err.response.follow_redirection
end

3
以下是我编写的代码(源自不同的示例),如果重定向太多将退出(请注意,ensure_success是可选的):
require "net/http"
require "uri"
class Net::HTTPResponse
  def ensure_success
    unless kind_of? Net::HTTPSuccess
      warn "Request failed with HTTP #{@code}"
      each_header do |h,v|
        warn "#{h} => #{v}"
      end
      abort
    end
  end
end
def do_request(uri_string)
  response = nil
  tries = 0
  loop do
    uri = URI.parse(uri_string)
    http = Net::HTTP.new(uri.host, uri.port)
    request = Net::HTTP::Get.new(uri.request_uri)
    response = http.request(request)
    uri_string = response['location'] if response['location']
    unless response.kind_of? Net::HTTPRedirection
      response.ensure_success
      break
    end
    if tries == 10
      puts "Timing out after 10 tries"
      break
    end
    tries += 1
  end
  response
end

1

不确定是否有人正在寻找这个确切的解决方案,但如果您正在尝试下载一个 http/https 图像并将其存储到变量中

require 'open_uri_redirections'

require 'net/https'

web_contents  = open('file_url_goes_here', :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :allow_redirections => :all) {|f| f.read }
puts web_contents

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接