2010-02-22 73 views
27

我希望有人能为我清理一些东西。我使用Rails 2.3.5,我可以在这样的控制器操作访问请求头:使用密钥HTTP_AUTHORIZATION而不是授权访问Ruby on Rails中的授权标头?

def index 
    if request.headers['...'] == '...' 
    ... 
    end 
end 

或类似的东西。 request.headers是ActionController::Http::Headers的一个实例,它似乎是一个Hash。因此,我期望,标题是关于我发送的名字的。但是,如果我发送一个请求,用Authorization头,就像这样:

curl -H 'Authorization: OAuth realm="MyRealm",...' http://app/path 

在动作下面的代码返回false:

if request.headers.include?('Authorization') ... 

而下面的回声出来的值我的头送:

render :text => request.headers['Authorization'] 

以下检查返回true,有趣的是:

if request.headers.include?('HTTP_AUTHORIZATION') ... 

同样,以下呼应了我的头发送的值:

render :text => request.headers['HTTP_AUTHORIZATION'] 

好像有一些神奇的发生,我是不知道的。对于为什么检查密钥'授权'失败,我完全感到困惑,但是呈现request.headers ['授权']的值成功。我也很困惑'HTTP_AUTHORIZATION'来自哪里,因为这是而不是我用请求发送的头部名称。任何人都知道到底发生了什么?

+0

我花了一个小时才弄清楚这一点。我认为这很令人困惑:至少他们应该更好地记录这种行为。 – collimarco 2015-12-29 12:00:02

回答

26

你是对的 - ActionController::Requestheaders方法返回ActionController::Http::Headers的一个实例,它从Hash继承。如果我们破解打开源,我们可以看到这一点:

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 

所以通过[]访问哈希表的时候,有一个第二次检查,看是否从env_name值(这只是upcases的关键,并预置HTTP_)存在。

这就是为什么你不能从request.headers.include?('Authorization')得到真实值 - include?在子类中没有被覆盖,以检查标题的正常版本和上升版本。我想你可能会步其后尘,并实现它自己是这样的:

module ActionController 
    module Http 
    class Headers < ::Hash 
     def include?(header_name) 
     self[header_name].present? 
     end 
    end 
    end 
end 

扔东西到lib/extensions/action_controller.rb什么的,要求它在environment.rb如果必要的话,你应该是好去。我建议只是修改控制器代码使用[]present?做检查,虽然:)

原因的头被upcased与HTTP_前缀,我相信,从机架,Rails的HTTP茎中间件。它可能会保持公正的情况下,另外预先HTTP_以避免与其他非标题环境的东西冲突。

所以,是的,有点神奇,但在看过源代码后不会太难理解,我总是会推荐它:) Rails有一些很好的源码,这些年来我学到了很多东西。

+0

谢谢布伦特!我会避免打鸭子,只使用礼物?像你的建议。仍然好奇原因,但你给了我一个开始寻找的好地方。 – 2010-02-26 03:13:55

+0

非常感谢你布伦特!这是非常棘手和怪异的,Rails自动大写和附加'HTTP_':( – 2014-04-11 06:50:05

4

根据The Common Gateway Interface RFC

"HTTP_"开头的名称元变量包含来自客户端的请求报头字段读取值 ,如果所使用的协议是HTTP 。 HTTP标题字段名称将转换为大写字母,将 中的所有出现的"-"替换为"_",并且将"HTTP_"前置 以给出元变量名称。