理想情况下,您会在 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