Маршрутизация Rails для обработки нескольких доменов в одном приложении

90

Мне не удалось найти работоспособное решение этой проблемы, несмотря на несколько похожих вопросов здесь и в других местах. Похоже, что на этот вопрос для Rails 3 нет ответа, так что вот:

У меня есть приложение, которое в настоящее время позволяет пользователям создавать свой собственный поддомен, содержащий их экземпляр приложения. В то время как в Rails 2 вам лучше всего служил гем subdomain-fu, в версии 3 это значительно проще, согласно Railscast - http://railscasts.com/episodes/221-subdomains-in-rails-3 .

Это хорошо, но я также хочу предоставить пользователям возможность связать собственное доменное имя со своей учетной записью. Поэтому, хотя у них может быть http://userx.mydomain.com , я бы хотел, чтобы они также выбрали ассоциированный http://userx.com .

Я нашел несколько ссылок на это в Rails 2, но эти методы, похоже, больше не работают (особенно этот: https://feefighters.com/blog/hosting-multiple-domains-from-a-single-rails -app / ).

Может ли кто-нибудь порекомендовать способ использования маршрутов для принятия произвольного домена и передачи его контроллеру, чтобы я мог показать соответствующий контент?

Обновление : теперь я получил большую часть ответа, благодаря своевременному ответу Леонида и свежему взгляду на код. В конечном итоге потребовалось дополнить существующий код поддомена, который я использовал (из решения Railscast), а затем добавить немного в routes.rb. Я еще не дошел до конца, но я хочу опубликовать то, что у меня есть.

В lib / subdomain.rb:

class Subdomain
  def self.matches?(request)
    request.subdomain.present? && request.subdomain != "www"
  end
end

class Domain
  def self.matches?(request)
    request.domain.present? && request.domain != "mydomain.com"
  end
end

Я добавил второй класс в имитацию первого, заведомо работающего. Я просто добавляю условие, которое гарантирует, что входящий домен не тот, для которого я размещаю основной сайт.

Этот класс используется в routes.rb:

require 'subdomain'
constraints(Domain) do
  match '/' => 'blogs#show'
end

constraints(Subdomain) do
  match '/' => 'blogs#show'
end

Здесь я добавляю к существующему коду поддомена (опять же, он отлично работает) строфу для проверки домена. Если этот сервер отвечает на этот домен, а не тот, под которым работает основной сайт, переадресация на указанный контроллер.

И хотя это, похоже, работает, у меня еще не все работает, но я думаю, что эта конкретная проблема решена.

Аарон Вег
источник
1
Большое спасибо за ваше редактирование, Аарон. Я имею дело с точно такой же ситуацией прямо сейчас. В качестве дополнительного вопроса, как заставить ваш сервер принимать любой домен, который ему перенаправляется? Я предполагаю, что это будет параметр в файле .conf, но я не уверен, что именно. Любая помощь будет оценена по достоинству!
мертвые
Аарон, я с тобой. Я хочу сделать то же самое. Но я не хочу жестко кодировать домен. Я хочу, чтобы все это выполнялось программно, без файлов зоны и перезапуска веб-сервера.
Michael K Madison
1
Майкл, тебе нужно решить проблему. Явно объявите и жестко запрограммируйте маршруты, которые предназначены исключительно для вашего приложения (например, для регистрации) с ограничением хоста или поддомена, а затем обработайте свои основные маршруты как «любой домен или поддомен». В этом случае ваши контроллеры обязаны найти текущий домен или субдомен и сопоставить его с нужным клиентом.
Джастин Френч

Ответы:

95

На самом деле в Rails 3 это проще, согласно http://guides.rubyonrails.org/routing.html#advanced-constraints :

1) определите собственный класс ограничения в lib/domain_constraint.rb:

class DomainConstraint
  def initialize(domain)
    @domains = [domain].flatten
  end

  def matches?(request)
    @domains.include? request.domain
  end
end

2) используйте класс в своих маршрутах с новым синтаксисом блока

constraints DomainConstraint.new('mydomain.com') do
  root :to => 'mydomain#index'
end

root :to => 'main#index'

или старомодный синтаксис опций

root :to => 'mydomain#index', :constraints => DomainConstraint.new('mydomain.com')
Леонид Шевцов
источник
6
Мне этот ответ кажется намного проще.
Джаред
7
Это отличное решение. Как это работает со средой разработки?
superluminary
2
@superluminary отлично работает, если вы настраиваете локальные домены для разработки (например, через /etc/hosts).
Леонид Шевцов
7
Примечание: если вы используете Pow локально и у вас есть mydomain.com.dev, то request.domainвозвращает .com.dev. Измените request.domainна, request.hostи он отлично работает.
Эрик Мейзер
2
Я обнаружил, что мне нужно создать безымянные маршруты, чтобы это работало, иначе я получаю сообщение об Invalid route name, already in use: 'root'ошибке ... Для этого я изменил маршрут наroot :to => 'mydomain#index', as: nil
Just Lucky Really
6

В Rails 5 вы можете просто сделать это в своих маршрутах:

constraints subdomain: 'blogs' do
  match '/' => 'blogs#show'
end
user3033467
источник