How can I rewrite a subdomain to another subdomain? - ruby-on-rails

I have this simple rack middleware written to rewrite my subdomain :
def call(env)
request = Rack::Request.new(env)
if request.host.starts_with?("something-something")
[301, { "Location" => request.url.gsub('something-something','site-i-want') }, self]
else
#app.call(env)
end
And this works fine on development. But in production I get an error calling .each for TheNameOfMyRackMiddleware
Is there something that looks strangely syntactically incorrect about how I'm writing this?
I want this someting-something.mywebsite.com to go to site-i-want.mywebsite.com
I also tried it directly with my routes with this :
constraints :subdomain => 'something-something' do
redirect { |p, req| req.url.sub('something-something', 'site-i-want') }
end
Which works fine on development. But doesn't route before I get my failure saying that the site does not exist.
Entirely open to anyway of getting this accomplished.

How about trying this in your routes:
constraints subdomain: 'something-something' do
get ':any', to: redirect(subdomain: 'site-i-want', path: '/%{any}'), any: /.*/
end
It basically catches any request to the 'something-something' domain, and rewrites it with the existing path, but new subdomain.

Related

Stop rails subdomain from triggering on *everything*

My site has some pages, such as example.com/about
I have also created an API for my app at api.example.com.
# API
namespace :api, path: '/', constraints: { subdomain: 'api' } do
scope format: true, constraints: { format: 'json' } do
get 'posts/latest', to: 'posts#latest'
get 'posts/:id', to: 'posts#show'
end
end
# Pages
get 'about' to: 'pages#about'
get 'contact', to: 'pages#contact'
The problem is, now my /about page is working on my api subdomain.
api.example.com/about should definitely not work like that, and should trigger a 404.
What I really want to do is wrap the entire rest of my routes file in constraints: { subdomain: nil } but I want to know the "Rails Way" to do this.
Thanks!
What I really want to do is wrap the entire rest of my routes file in constraints: { subdomain: nil } but I want to know the "Rails Way" to do this.
This is the Rails way.
A typical Rails app will just listen to a socket and not care about domains. Usually your webserver will handle the mapping between domains/IP addresses and internal application sockets (e.g. Apache VirtualHost).
When the webserver passes an HTTP request to the application, Rails then is only interested in everything but the (sub-) domain, unless configured otherwise.

Multiple 'root to' routes in rails 4.0

I am trying to get rails to go to different controller#action according to the subdomain, and this is what I have so far in routes.rb
Petworkslabs::Application.routes.draw do
get '/', to: 'custom#show', constraints: {subdomain: '/.+/'}, as: 'custom_root'
get '/', to: "welcome#home", as: 'default_root'
end
rake shows the correct routes I want it to take
rake routes
Prefix Verb URI Pattern Controller#Action
custom_root GET / custom#show {:subdomain=>"/.+/"}
default_root GET / welcome#home
But for some reason, I can't get requests like abc.localhost:3000 to hit the custom controller. It always routes it to welcome#home. Any ideas? I am fairly new to rails, so any tips about general debugging would also be appreciated.
EDIT: I stepped through the code using the debugger and this is what I found
(rdb:32) request.domain
"abc.localhost"
(rdb:32) request.subdomain
""
(rdb:32) request.subdomain.present?
false
Looks like for some reason rails thinks that the subdomain is not present, even though its there. I wonder if its because I am doing this localhost.
Updated Answer:
Worked for me on Rails 3 & 4:
get '/' => 'custom#show', :constraints => { :subdomain => /.+/ }
root :to => "welcome#home"
#manishie's answer is right, but you'll still likely have issues in your devo environment if you're using localhost. To fix it add the following line to config/environments/development.rb:
config.action_dispatch.tld_length = 0
and then use #manishie's answer in routes.rb:
get '/' => 'custom#show', :constraints => { :subdomain => /.+/ }
root :to => "welcome#home"
The issue is that tld_length defaults to 1 and there's no domain extension when you're using localhost so rails fails to pickup the subdomain. pixeltrix explains it really well here: https://github.com/rails/rails/issues/12438
For some reason request.subdomain was not getting populated at all (I suspect this is because I have doing this on localhost, I have opened a bug here https://github.com/rails/rails/issues/12438). This was causing the regex match in routes.rb to fail. I ended up creating a custom matches? method for Subdomain which looks something like this
class Subdomain
def self.matches?(request)
request.domain.split('.').size>1 && request.subdomain != "www"
end
end
and hooking that up in routes.rb
constraints(Subdomain) do
get '/', to: "custom#home", as: 'custom_root'
end
this seems to work.
EDIT: More information in the github issues page https://github.com/rails/rails/issues/12438

Rails 3 redirect to external page with parameters

I have this link:
railsapp/api/details.php?id=1&app=dvd
That I need to redirecto to another page in an external non-rails server:
non-railsapp/api/details.php?id=1&app=dvd
If I put this in the routes.rb file:
match '/api/details.php' => redirect("non-railsapp/api/details.php")
The redirection will work, but the parameters will not be passed. What magic needs to be there so that the params are passed as well?
In Fact, what would be great would be to be able to redirect everything after the /api/ to the external site with all parameters. That would make my life a lot easier.
I've read the documentation on Rails Routing but cannot find any examples of something like this.
Thanks.
Try something like this:
get "/api/details.php/:id/:app" => redirect{ |p, req| "non-railsapp/api/details.php/#{p[:id]}/#{p[:app]}" }
Edit:
You could also make the parameters optional:
get "/api/details.php(/:id(/:app))" => redirect{ |p, req| "non-railsapp/api/details.php(/#{p[:id]}(/#{p[:app]}))" }
You can do the following:
url = URI("http://www.google.com/")
url.query = URI.encode_www_form(:q => "query")
#redirecting to http://www.google.com/?q=query
redirect_to url.to_s

Rails routes redirection for subdomains

We can't change the server configuration files, so we need to do our redirections at the rails level.
I have no problem with path redirections to external sites, like:
match "/meow" => redirect("http://meow.com/")
The issue is with the subdomains. I need to redirect for example:
http://my.example.com => http://example.com
How can this be done using routes.rb?
According to #cfernandezlinux's amazing answer, here's the same in Rails 4/Ruby 2 syntax:
constraints subdomain: "meow" do
get "/" => redirect { |params| "http://www.externalurl.com" }
end
match in routes.rb is not allowed in Rails 4.0 anymore. You have to use explicitly get, post, etc.
hashrocket syntax (=>) is for old Ruby, now in Ruby 2.0 we use param: 'value' syntax
I ended up doing something like this:
constraints :subdomain => "meow" do
match "/" => redirect { |params| "http://www.externalurl.com" }
end
If you don't want to hard-code the URL (so, for example, you can test/use this locally), you could do:
constraints subdomain: 'subdomain_from' do
get '/' => redirect(subdomain: :new_subdomain, path: '')
end
So now subdomain_from.google.com will redirect to new_subdomain.google.com.

How do I redirect without www using Rails 3 / Rack?

I understand there are a lot of questions that answer this. I'm familiar with .htaccess and nginx.conf methods, but I do not have access to such traditional configuration methods on Heroku.
Simone Carletti gave this answer that leverages Rails 2.x Metals, but I'm using Rails 3 and this isn't compatible:
Redirect non-www requests to www URLs in Ruby on Rails
Please note:
I'm not looking for a simple before_filter in my ApplicationController. I'd like to accomplish a rewrite similar to Simone's. I believe this is job for the webserver or middleware like Rack at the very least, so I'd like to leave this bit out of the actual application code.
Goal
redirect to status
----------------------------------------------------
www.foo.com foo.com 301
www.foo.com/whatever foo.com/whatever 301
Only hosts matching /^www\./ should be redirected. All other requests should be ignored.
In Ruby on Rails 4, removing www. from any URL whilst maintaining the pathname can be achieved simply by using:
# config/routes.rb
constraints subdomain: 'www' do
get ':any', to: redirect(subdomain: nil, path: '/%{any}'), any: /.*/
end
In contrast, adding www. to the beginning of any URL that doesn't already have it can be achieved by:
# config/routes.rb
constraints subdomain: false do
get ':any', to: redirect(subdomain: 'www', path: '/%{any}'), any: /.*/
end
There's a better approach if you're using Rails 3. Just take advantage of the routing awesomeness.
Foo::Application.routes.draw do
constraints(:host => /^example.com/) do
root :to => redirect("http://www.example.com")
match '/*path', :to => redirect {|params| "http://www.example.com/#{params[:path]}"}
end
end
I really like using the Rails Router for such things. Previous answers were good, but I wanted something general purpose I can use for any url that starts with "www".
I think this is a good solution:
constraints(:host => /^www\./) do
match "(*x)" => redirect { |params, request|
URI.parse(request.url).tap {|url| url.host.sub!('www.', '') }.to_s
}
end
Take a look at this middleware, it should do precisely what you want:
http://github.com/iSabanin/www_ditcher
Let me know if that worked for you.
A one-line version of Duke's solution. Just add to the top of routes.rb
match '(*any)' => redirect { |p, req| req.url.sub('www.', '') }, :constraints => { :host => /^www\./ }
In Rails 3
#config/routes.rb
Example::Application.routes.draw do
constraints(:host => "www.example.net") do
match "(*x)" => redirect { |params, request|
URI.parse(request.url).tap { |x| x.host = "example.net" }.to_s
}
end
# ....
# .. more routes ..
# ....
end
If you want to redirect from the top-level domain (TLD) to the www subdomain, use this code:
constraints :subdomain => '' do
match '(*any)' => redirect { |p, req| req.url.sub('//', '//www.') }
end
Note: This code the use of sub, not gsub, because sub replaces the first occurrence of the double-slashes where gsub would replace all double-slashes.
For Rails 4 the above solutions have to be appended with the Verb construction e.g. via: [:get, :post]. Duke's solution becomes:
constraints(:host => /^www\./) do
match "(*x)" => redirect { |params, request|
URI.parse(request.url).tap {|url| url.host.sub!('www.', '') }.to_s
}, via: [:get, :post]
end
Nothing wrong with the approaches above, but there are also a couple of gems that provide Rack middleware to do this.
I like the way that they keep this behaviour separate from the app itself, but it's not a particularly strong argument either way. I also use middleware to do this when working with Sinatra, so prefer to use a technique that I can use on apps built from Rails and/or Sinatra (I often run Nesta embedded in Rails).
Anyway, here they are:
https://github.com/cwninja/rack-force_domain
https://github.com/tylerhunt/rack-canonical-host
The first is simpler (and the one I've been using) while the second offers a couple more features (that I'm yet to need, but appreciate).

Resources