如何使用http.get保留案例?
我需要在特定的字符大小写中发送HTTP标头。 我知道这是针对RFC的,但我有一个要求。
http.get
似乎改变了我提供的头字典的情况。 我该如何保留角色?
根据Tin Man的回答, Net::HTTP
库在您的自定义标头密钥(以及所有标头密钥)上调用#downcase
,这里有一些其他选项不会修补整个Net::HTTP
。
你可以试试这个:
custom_header_key = "X-miXEd-cASe" def custom_header_key.downcase self end
要避免清除方法缓存,请将上述结果存储在类级别常量中:
custom_header_key = "X-miXEd-cASe" def custom_header_key.downcase self end CUSTOM_HEADER_KEY = custom_header_key
或子类String以覆盖该特定行为:
class StringWithIdentityDowncase < String def downcase self end end custom_header_key = StringWithIdentityDowncase.new("X-miXEd-cASe")
接受的答案不起作用。 坦率地说,我怀疑它曾经做过,因为看起来它不得不覆盖拆分和大写 ,我跟着那个方法做了几次提交,至少自2004年以来就是这样。
这是我的解决方案,回答这个封闭的问题:
require 'net/http' class Net::HTTP::ImmutableHeaderKey attr_reader :key def initialize(key) @key = key end def downcase self end def capitalize self end def split(*) [self] end def hash key.hash end def eql?(other) key.eql? other.key.eql? end def to_s key end end
现在,您需要确保始终使用此类的实例作为键。
request = Net::HTTP::Get.new('/') user_key = Net::HTTP::ImmutableHeaderKey.new("user") request[user_key] = "James" require 'stringio' StringIO.new.tap do |output| request.exec output, 'ver', 'path' puts output.string end # >> GET path HTTP/ver # >> Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3 # >> Accept: */* # >> User-Agent: Ruby # >> user: James # >>
我是一种方法,但我建议按照@yfeldblum的建议来做这件事,只需简单地为需要单独保留案例的标题键短路。
在Net :: HTTP :: HTTPHeader中的多个位置,标题使用downcase
折叠为小写。
我认为改变这种行为非常激烈,但这样做会有所改变。 将其添加到您的源中,它将重新定义HTTPHeader模块中具有downcase
的方法。
module HTTPHeader def initialize_http_header(initheader) @header = {} return unless initheader initheader.each do |key, value| warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE @header[key] = [value.strip] end end def [](key) a = @header[key] or return nil a.join(', ') end def []=(key, val) unless val @header.delete key return val end @header[key] = [val] end def add_field(key, val) if @header.key?(key) @header[key].push val else @header[key] = [val] end end def get_fields(key) return nil unless @header[key] @header[key].dup end def fetch(key, *args, &block) #:yield: +key+ a = @header.fetch(key, *args, &block) a.kind_of?(Array) ? a.join(', ') : a end # Removes a header field. def delete(key) @header.delete(key) end # true if +key+ header exists. def key?(key) @header.key?(key) end def tokens(vals) return [] unless vals vals.map {|v| v.split(',') }.flatten\ .reject {|str| str.strip.empty? }\ .map {|tok| tok.strip } end end
我认为这是一种蛮力的方式来实现它,但没有别的更优雅跳跃到脑海。
虽然这应该解决使用Net :: HTTP的任何Ruby库的问题,但对于使用Curl或libcurl的任何gem,它可能会失败。
Joshua Cheek的答案很棒,但它在Ruby 2.3中已经完成了
此修改修复它:
class Net::HTTP::ImmutableHeaderKey ... def to_s caller.first.match(/capitalize/) ? self : @key end end
这一切都落入了net / generic_request#write_header。 你可以修补代码
# 'net/generic_request' line 319 def write_header(sock, ver, path) customheaders = { "My-Custom-Header" => "MY-CUSTOM-HEADER", "Another-Custom-Header" => "aNoThErCuStOmHeAdEr" } buf = "#{@method} #{path} HTTP/#{ver}\r\n" each_capitalized do |k,v| customheaders.key?(k) ? kk = customheaders[k] : kk = k buf << "#{kk}: #{v}\r\n" end buf << "\r\n" sock.write buf end
并且您不需要重写整个net / http / header,net / generic_request和net / http链。 这不是最好的解决方案,但它是我猜的最简单的解决方案,并且修补猴子的数量最少。
希望能帮助到你。