- Rails3と4のrequest.headers周りを調べることがあったのでメモっておく
- コードリーディングとして楽しかったw
比較
Rails3
actionpack-3.2.22/lib/action_dispatch/http/headers.rb
1 module ActionDispatch
2 module Http
3 class Headers < ::Hash
4 @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }
5
6 def initialize(*args)
7
8 if args.size == 1 && args[0].is_a?(Hash)
9 super()
10 update(args[0])
11 else
12 super
13 end
14 end
15
16 def [](header_name)
17 if include?(header_name)
18 super
19 else
20 super(env_name(header_name))
21 end
22 end
23
24 private
25 # Converts a HTTP header name to an environment variable name.
26 def env_name(header_name)
27 @@env_cache[header_name]
28 end
29 end
30 end
31 end
- リクエストヘッダを@envに格納し、[]()で呼び出される際に2種類のキーで関連付けられた値を探します
- 探す順番
- 元のキーの値
- HTTP_xxxに変換した値
- 例えばrequest.headers['Hogehoge']を実行すると'Hogehoge','HTTP_HOGEHOGE'の順に@envから探され先にヒットしたほうが返ります
- 探す順番
- []=が定義されておらず、親クラスのHash#[]=が実行されます
- request.headers['Hogehoge']='Hogehoge' としても request.headers['HTTP_HOGEHOGE'] はnilとなります
Rails4
actionpack-4.1.11/lib/action_dispatch/http/headers.rb
1 module ActionDispatch
2 module Http
3 class Headers
4 CGI_VARIABLES = %w(
5 CONTENT_TYPE CONTENT_LENGTH
6 HTTPS AUTH_TYPE GATEWAY_INTERFACE
7 PATH_INFO PATH_TRANSLATED QUERY_STRING
8 REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER
9 REQUEST_METHOD SCRIPT_NAME
10 SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
11 )
12 HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
...
17 def initialize(env = {})
18 @env = env
19 end
...
21 def [](key)
22 @env[env_name(key)]
23 end
24
25 def []=(key, value)
26 @env[env_name(key)] = value
27 end
...
54 private
55 def env_name(key)
56 key = key.to_s
57 if key =~ HTTP_HEADER
58 key = key.upcase.tr('-', '_')
59 key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
60 end
61 key
62 end
63 end
64 end
65 end
-
@envにリクエストヘッダが格納されるのはRails3と同じですが[]()の実装が違っています
- 常にHTTP_xxxの方で関連付けられた値を求めるようになっています
- request.headers['Hoge']とrequest.headers['HTTP_HOGE']は常に同じ結果となります
- []=が定義されています
- Rails4ではkeyを"HTTP_*"形式に変換します
- request.headers['Hoge']='hoge' で設定した値が request.headers['HTTP_HOGE'] で取得できます
まとめ
- 原則的にはRails3,4で同じ使い方が出来ますが以下の様な場合に差が出てきます
- request.headers['Hoge']='hoge';request.headeres['HTTP_HOGE'] の結果が違ってきます
- 上記の通り
- Rails3はnilとなります
- テストなどでhedears中に'Hoge','HTTP_HOGE'が両方が設定された場合
- Rails3ではrequest.headers['Hoge'], request.headers['HTTP_HOGE'] でそれぞれの値が取得できます
- Rails4では常に 'HTTP_HOGE' で設定した値が取得されます
- テストなどで狙って書かない限りはこのようにはならないはずです、もしテストでリクエストヘッダーを弄る場合は"HTTP_*"形式で書いておくと不要なトラブルを避けられそうです
- request.headers['Hoge']='hoge';request.headeres['HTTP_HOGE'] の結果が違ってきます
感想
- ActionDispatchctionDispatch::Http::Headers#[]=が定義されたのが良いですね!
- Rails3で許されていた曖昧な書き方がなくなり、Rails4の方がミスりにくくなっているように思います