【问题标题】:Rack::Request - how do I get all headers?Rack::Request - 如何获取所有标头?
【发布时间】:2011-09-13 03:31:43
【问题描述】:

标题是不言自明的。有什么方法可以获取标题(Rack::Request.env[] 除外)?

【问题讨论】:

    标签: ruby http http-headers rack


    【解决方案1】:

    HTTP 标头在传递给您的应用的Rack environment 中可用:

    HTTP_ 变量:对应于客户端提供的 HTTP 请求标头的变量(即名称以 HTTP_ 开头的变量)。这些变量的存在与否应该与请求中相应 HTTP 标头的存在与否相对应。

    因此 HTTP 标头以“HTTP_”为前缀并添加到哈希中。

    这是一个提取并显示它们的小程序:

    require 'rack'
    
    app = Proc.new do |env|
      headers = env.select {|k,v| k.start_with? 'HTTP_'}
        .collect {|key, val| [key.sub(/^HTTP_/, ''), val]}
        .collect {|key, val| "#{key}: #{val}<br>"}
        .sort
      [200, {'Content-Type' => 'text/html'}, headers]
    end
    
    Rack::Server.start :app => app, :Port => 8080
    

    当我运行此程序时,除了 Chrome 或 Firefox 显示的 HTTP 标头外,还有一个“VERSION:HTPP/1.1”(即一个带有键“HTTP_VERSION”和值“HTTP/1.1”的条目正在添加到 env 哈希)。

    【讨论】:

    • 啊,所以基本上是env :)。我不喜欢的是替换了一些字符的大写名称。好吧,我想我将不得不摆脱它......
    • @PJK 无论如何,名称应该不区分大小写:w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2。什么字符被替换?您是否尝试使用 ASCII 字符集之外的字符?标题名称只能是 ASCII。
    • 我知道,这只是为了方便...例如,X-Build 变成 HTTP_X_BUILD,这意味着 X_Build 和 X-BUILD 应该是等价的,但是(我被告知)浏览器区分这两种选择。
    • @PJK 我明白了:- 正在更改为 _。我想这是为了与 CGI 保持兼容(环境变量不能包含-)。但 response 标头不应该受到影响。
    • 未损坏的机架环境文档链接:github.com/rack/rack/blob/master/SPEC.rdoc#the-environment-
    【解决方案2】:

    基于@matt 的回答,但这确实为您提供了问题中所要求的散列中的请求标头:

    headers = Hash[*env.select {|k,v| k.start_with? 'HTTP_'}
      .collect {|k,v| [k.sub(/^HTTP_/, ''), v]}
      .collect {|k,v| [k.split('_').collect(&:capitalize).join('-'), v]}
      .sort
      .flatten]
    

    根据您喜欢的键约定,您可能想要使用其他内容而不是 :capitalize。

    【讨论】:

      【解决方案3】:

      像@Gavriel 的回答,但使用transform_keys(更简单):

      class Request
        def headers
          env.select { |k,v| k.start_with? 'HTTP_'}.
            transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }
        end
      end
      

      您甚至可以做到这一点,即使大小写不同,查找仍然有效:

        def headers
          env.
            select { |k,v| k.start_with? 'HTTP_'}.
            transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }.
            sort.to_h.
            tap do |headers|
              headers.define_singleton_method :[] do |k|
                super(k.split(/[-_]/).map(&:capitalize).join('-'))
              end
            end
        end
      

      例如,即使headers 规范化了键,它也会返回:

      {
        Dnt: '1',
        Etag: 'W/"ec4454af5ae1bacff1afc5a06a2133f4"',
        'X-Xss-Protection': '1; mode=block',
      }
      

      您仍然可以使用这些标头的更自然/通用名称来查找标头:

      headers['DNT']
      headers['ETag']
      headers['X-XSS-Protection']
      

      【讨论】:

      • 在你的例子中,不应该是'Dnt': '1'而不是Dnt: '1'吗?
      • @RonKlein Dnt: 是正确的。使用 'Dnt': 是 JSON 而不是 ruby​​。
      • @MarlinPierce 好吧,它在语法上是正确的 Ruby,但它不是代码产生的。 @RonKlein 是对的,应该是 'Dnt': '1', 这里的键是从头到尾的字符串。大写的文字表示 Ruby 中的常量。
      • @ArnaudMeuret 现在,我认为您正在混淆常量和文字。在 ruby​​ 中,'Dnt': 解析为符号 :Dnt。如果你想要字符串键,你需要{ 'Dnt' =&gt; '1' }。这将告诉您'Dnt': 是一个符号{ 'Dnt': '1' }.each_pair { |key, value| puts key.inspect }
      • @ArnaudMeuret 另一种看待这一点的方式是,Dnt='Knock';{ 'Dnt': 1, 'Dnt' =&gt; 2, Dnt =&gt; 3 } 的计算结果为 {:Dnt=&gt;1, "Dnt"=&gt;2, "Knock"=&gt;3}
      猜你喜欢
      • 2013-10-25
      • 1970-01-01
      • 2014-09-19
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      • 2011-04-22
      相关资源
      最近更新 更多