理想的には、Web サーバー構成でそのようなルールを設定します。リクエストは Rails スタックにさえ到達しないため、より高速になります。アプリにコードを追加する必要もありません。
ただし、heroku などの制限された環境で実行している場合は、ラック ミドルウェアを追加することをお勧めします。(ガイドラインとして、この特定のコードにバグがないかどうかは保証できません)
class Redirector
SUBDOMAIN = 'www'
def initialize(app)
@app = app
end
def call(env)
@env = env
if redirect?
redirect
else
@app.call(env)
end
end
private
def redirect?
# do some regex to figure out if you want to redirect
end
def redirect
headers = {
"location" => redirect_url
}
[302, headers, ["You are being redirected..."]] # 302 for temp, 301 for permanent
end
def redirect_url
scheme = @env["rack.url_scheme"]
if @env['SERVER_PORT'] == '80'
port = ''
else
port = ":#{@env['SERVER_PORT']}"
end
path = @env["PATH_INFO"]
query_string = ""
if !@env["QUERY_STRING"].empty?
query_string = "?" + @env["QUERY_STRING"]
end
host = "://#{SUBDOMAIN}." + domain # this is where we add the subdomain
"#{scheme}#{host}#{path}#{query_string}"
end
def domain
# extract domain from request or get it from an environment variable etc.
end
end
全体を個別にテストすることもできます
describe Redirector do
include Rack::Test::Methods
def default_app
lambda { |env|
headers = {'Content-Type' => "text/html"}
headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
[200, headers, ["default body"]]
}
end
def app()
@app ||= Rack::Lint.new(Redirector.new(default_app))
end
it "redirects unsupported subdomains" do
get "http://example.com/zomg?a=1"
last_response.status.should eq 301
last_response.header['location'].should eq "http://www.example.com/zomg?a=1"
end
# and so on
end
次に、それを本番環境 (または任意の優先環境) にのみ追加できます
# production.rb
# ...
config.middleware.insert_after 'ActionDispatch::Static', 'Redirector'
開発中にテストしたい場合は、同じ行を development.rb に追加し、hosts ファイル (通常は /etc/hosts) にレコードを追加して、yoursubdomain.localhost を 127.0.0.1 として扱います。