我需要在特定字符大小写中发送HTTP标头。我知道这是针对RFC的,但我有一个要求。
http.get
似乎改变了我提供的头字典的情况。我该如何保留角色?
答案 0 :(得分:13)
基于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")
答案 1 :(得分:5)
接受的答案不起作用。坦率地说,我怀疑它曾经做过,因为看起来它也必须覆盖split and capitalize,我跟着那个方法做了几次提交,至少自2004年以来就是这样。
以下是我的解决方案,回答this封闭式问题:
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
# >>
答案 2 :(得分:2)
Mine是这样做的一种方法,但我建议按照@yfeldblum的建议进行操作,只需将downcase
短路用于需要单独保留其案例的标题键。
在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来说,它可能会失败。
答案 3 :(得分:1)
Joshua Cheek的答案很棒,但它在Ruby 2.3中已经完成了工作
此修改修复了它:
class Net::HTTP::ImmutableHeaderKey
...
def to_s
caller.first.match(/capitalize/) ? self : @key
end
end
答案 4 :(得分:0)
这一切都落入了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链。 它不是最好的解决方案,但它是我猜的最简单的解决方案,而且猴子补丁的数量最少。
希望它有所帮助。