Ruby on Railsでホスト名によるコントローラー振り分け - route.rb
さて、いよいよ僕のRuby on Rails覚え書きも、怪しいものになってまいりました。
この日記のタイトル
「Ruby on Railsでホスト名によるコントローラー振り分け」
という意味ですが、通常Ruby on Railsでは
http://ホスト名.ドメイン名/コントローラー名/アクション名/
のようなURLの解釈をされるということは、皆さんご存知のことだと思いますが、この解釈部分を変更して以下のようにするにはどうすれば良いのかということをノウハウとして掲載しているものです。
http://コントローラー名.ドメイン名/アクション名/好きな名前/
もしくはRESTfulにしたいのであれば、以下のようなものも可能です。(共通の改造をした後に、route.rbで制御するだけですが・・・)
http://コントローラー名.ドメイン名/名詞/アクション名
さて、やり方は至って簡単!!
まず、ActionControllerまわりを拡張します。
###################################################### # hostname の指定によってルーティングできるように設定します ###################################################### module ActionController module Routing class RouteSet def extract_request_environment(request) { :method => request.method, :hostname => request.env['HTTP_HOST'].split('.').first } end end class Route alias_method :old_recognition_conditions, :recognition_conditions def recognition_conditions result = old_recognition_conditions result << "conditions[:hostname] === env[:hostname]" if conditions[:hostname] result end end end end
もういっちょ!
###################################################### # ActionController.url_forを使用した時に、hostnameの値を # 利用してURLを作成するように設定します ###################################################### class ActionController::Base # :controller に指定された値を、hostnameに変換してURLを作成する def url_for(options = nil) if options[:controller] # オプション中に:controllerの指定がある場合、その値をホスト名に変換する options[:host] = get_full_hostname(options[:controller]) else # :controllerが指定されていない場合は、現在リクエスト中のHTTP_HOSTのホスト名を使用する options[:host] = get_full_hostname(request.env['HTTP_HOST'].split('.').first) end # 必ずフルパスになるように設定 options[:only_path] = false # もともと設定されていた:controllerは不要のため、削除する options[:controller] = '' # アンエスケープは一番最後に実施します unescape_option = options[:unescape] options.delete(:unescape) result = original_url_for(options) # アンエスケープ実行 if unescape_option == true CGI::unescape(result) else result end end private ################################## # オリジナルのurl_for ################################## def original_url_for(options = nil) case options || {} when String options when Hash @url.rewrite(rewrite_options(options)) else polymorphic_url(options) end end ################################## # ホスト名にドメイン名をつけたものを返す # 80番ポートかどうかは開発用 # ※MYSITE_DOMAIN_NAMEはドメイン名の定数 ################################## def get_full_hostname(hostname) if RAILS_ENV != 'production' "#{hostname}.#{MYSITE_DOMAIN_NAME}:#{request.env['SERVER_PORT']}" else "#{hostname}.#{MYSITE_DOMAIN_NAME}" end end end
これの2つのコードを、どっかのファイルに書いておいてinitializerでrequireしておけばOKです。
ちなみに、ここではコードを削って掲載していますが、リバースプロキシを仕込んだ本番環境の場合、ブラウザからのリクエストがhttpsかどうかrailsで判別できないため、httpsの場合はリバースプロキシからの転送にHTTPヘッダ情報(「ブラウザからのリクエストはhttpsだよ!」)を追加して、only_pathをfalseに設定しなければならない制約を回避する必要があります。
そして最後にroute.rbを編集し、ホスト名でコントローラーのルーティングを設定してあげればOK。
map.connect ':action/:id', :controller => 'diary', :conditions => {:hostname => d'}
map.connect ':action/:id', :controller => 'photo', :conditions => {:hostname => f'}
もうちょっと難しく・・・
map.connect ':user_id/:date', :controller => 'diary', :action => 'story',
:conditions => {:hostname => 'd'},
:requirements => {:date => /[0-9]{8}/}
見たいな感じで設定すると
http://d.ドメイン名/yukizm/20080214/
てな感じで、呼び出しが出来て、この時に diaryコントローラーのstoryメソッドが実行されるようになります。
当然 params[:user_id]には 'yukizm'、params[:date]には '20080214'が入ってます。