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 对象。。。这个特性我都没有找到对应的文档