You are correct - the headers
method of ActionController::Request
returns an instance of ActionController::Http::Headers
, which is inherits from Hash. If we crack open the source, we see this:
class Headers < ::Hash
extend ActiveSupport::Memoizable
def initialize(*args)
if args.size == 1 && args[0].is_a?(Hash)
super()
update(args[0])
else
super
end
end
def [](header_name)
if include?(header_name)
super
else
super(env_name(header_name))
end
end
private
# Converts a HTTP header name to an environment variable name.
def env_name(header_name)
"HTTP_#{header_name.upcase.gsub(/-/, '_')}"
end
memoize :env_name
end
So when accessing the Hash via []
, there's a second check to see if value from env_name
(which just upcases the key and prepends HTTP_
) exists.
This is why you can't get a true value from request.headers.include?('Authorization')
-- include?
is not overridden in the subclass to check for both the normal and upcased version of the header. I imagine you could follow suit and implement it yourself like this:
module ActionController
module Http
class Headers < ::Hash
def include?(header_name)
self[header_name].present?
end
end
end
end
Throw that into lib/extensions/action_controller.rb
or something, require it in environment.rb
if necessary, and you should be good to go. I'd recommend just modifying your controller code to use []
and present?
to do the check, though :)
The reason that the headers are upcased and prefixed with HTTP_
, I believe, stems from Rack, Rails' HTTP middleware. It probably does this to remain impartial about case, additionally prepending HTTP_
to avoid conflicts with other non-header environment stuff that comes in.
So, yes, a bit magical, but not too hard to understand after glancing at the source, which I'd always recommend :) Rails has some very nice source that I've learned a lot from over the years.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…