Ruby 又踩了个 open-uri 的坑,老库的 API 有时候真是摸不着头脑

spike76 · 2021年07月06日 · 最后由 linlinda 回复于 2023年11月03日 · 1333 次阅读

遭遇的问题

ruby 2.7,一段简单的代码,用 open-uri 通过代理去请求百度。此时运行正常

require "open-uri"
URI.parse("http://www.baidu.com").open(proxy: "your_proxy")

当我想加个 user-agent 的请求头时,自然很容易想到在 open 里再加个选项,变成

require "open-uri"
URI.parse("http://www.baidu.com").open(proxy: "your_proxy", "User-Agent": "new_agent")

此时报错:ArgumentError (unrecognized option: User-Agent) 很早之前我就用 open-uri 做过一样的功能,当时确实成功修改了 User-Agent,为啥这次不行了勒。难道还跟大小写有关???

处理过程

先看看 api 文档,方法签名是这样的open(*rest, &block) ,感觉这个签名没啥有用的信息,完全摸不着头脑。。。于是直接看源码,找到了这个

def OpenURI.check_options(options) # :nodoc:
  options.each {|k, v|
    next unless Symbol === k
    unless Options.include? k
      raise ArgumentError, "unrecognized option: #{k}"
    end
  }
end

Options = {
  :proxy => true,
  :proxy_http_basic_authentication => true,
   ...太长省略
}

当 options 的键是 Symbol 时,会检查该键是否在 Options 定义的内容中。推断把键改成 String 应该就 ok 了,于是写成这样

URI.parse("http://www.baidu.com").open(proxy: "your_proxy", "User-Agent"=>"new_agent")

果然不报错了。。。这里的选项根据键的类型分成了两类,Symbol 类的有功能意义,String 类的会被当做 http 的 header。这种特性应该会在文档里有表述,于是又回头看文档,找到了这句话Each option with a string key specifies an extra header field for HTTP. I.e., it is ignored for FTP without HTTP proxy. The hash may include other options, where keys are symbols 是我大意了啊

为什么是“又”

因为之前就踩过另一个 open-uri 的坑,线下多次用 open 请求不同的图片文件,都返回的是 Tempfile 对象,于是按文件对象继续处理。上线运行一段时间,报错:StringIO 对象没有某某方法。原来 open 在请求小文件的时候返回 StringIO 对象。。。这个特性我都没有找到对应的文档

用 net/http 不香吗?

这个太多东西不明确。

pynix 回复

net/http支持更多操作,但需要手动跟踪重定向。简单 get 请求习惯用 open-uri

spike76 回复

循环重定向不久凉了。

pynix 回复

默认检测到循环重定向 会抛异常。也可以手动关闭跟踪重定向功能,自己处理重定向

原来 open 在请求小文件的时候返回 StringIO 对象。。。这个特性我都没有找到对应的文档

这个坑我也踩过😂

HTTParty yyds

来 open 在请求小文件的时候返回 StringIO 对象

原理上,这对于一个默认直接返回 String 的 API 是一个很难处理的问题。因为假设所有情况都返回 String 的话,读取到 Content-Length > Body Size 的话就意味着要继续读取,直到读完为止才能返回。而理论上 HTTP 的 Content-Length 上 GB 上 TB 都没有问题,所以大多数库的返回是默认 StringIO,根据你具体需要不需要读,甚至需要不需要流式读取自行决定怎么处理。

真巧,那个返回对象的问题我也踩过

貌似系统自带的网络库都不怎么样,象 python 不停地出 urllib1 urllib2 urlib3 马上要出 urllib4 仍然挡不住码农用社区版本的 requests

楼上好多坑友。。。

一个项目要是 gem 多了,会发现底层有各种第三方的网络库,各用各的。大家都不想用自带的

需要 登录 后方可回复, 如果你还没有账号请 注册新账号