Roteamento Rails para lidar com vários domínios em um único aplicativo

90

Não consegui encontrar uma solução viável para esse problema, apesar de várias perguntas semelhantes aqui e em outros lugares. Parece provável que esta questão não tenha sido respondida para Rails 3, então aqui vai:

Eu tenho um aplicativo que atualmente permite que os usuários criem seu próprio subdomínio que contém sua instância do aplicativo. Enquanto no Rails 2 você foi melhor servido usando a gem subdomain-fu, na versão 3 é dramaticamente mais simples, de acordo com o Railscast - http://railscasts.com/episodes/221-subdomains-in-rails-3 .

Isso é bom, mas também quero oferecer a opção para os usuários associarem seu próprio nome de domínio à conta. Então, embora eles possam ter http://userx.mydomain.com , eu gostaria que eles escolhessem ter http://userx.com associado também.

Eu encontrei algumas referências a fazer isso no Rails 2, mas essas técnicas parecem não funcionar mais (particularmente este aqui: https://feefighters.com/blog/hosting-multiple-domains-from-a-single-rails -app / ).

Alguém pode recomendar uma maneira de usar rotas para aceitar um domínio arbitrário e passá-lo para um controlador para que eu possa mostrar o conteúdo apropriado?

Atualização : recebi a maior parte das respostas agora, graças à resposta oportuna de Leonid e uma nova visão do código. No final das contas, ele exigiu uma adição ao código de subdomínio existente que eu estava usando (da solução Railscast) e, em seguida, adicionando um pouco a routes.rb. Ainda não cheguei lá, mas quero postar o que tenho até agora.

Em 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

Eu adicionei a segunda classe em imitação da primeira, que está funcionando. Eu simplesmente adiciono uma condição que garante que o domínio de entrada não seja aquele para o qual estou hospedando o site principal.

Esta classe é usada em routes.rb:

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

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

Aqui, estou prefixando o código de subdomínio existente (novamente, está funcionando bem) com uma estrofe para verificar o domínio. Se este servidor responder a esse domínio e não for aquele sob o qual o site principal opera, encaminhe para o controlador especificado.

E embora isso pareça estar funcionando, não tenho tudo funcionando ainda, mas acho que esse problema específico foi resolvido.

Aaron Vegh
fonte
1
Muito obrigado pela sua edição, Aaron. Estou lidando exatamente com a mesma situação agora. Como uma pergunta de acompanhamento, como você faz com que seu servidor aceite qualquer domínio que esteja sendo encaminhado para ele? Suponho que seria uma configuração no arquivo .conf, mas não tenho certeza do que. Qualquer ajuda seria apreciada!
morto em
Aaron, estou com você. Eu quero fazer a mesma coisa. Mas não quero codificar o domínio. Quero que tudo seja feito programaticamente, sem arquivos de zona e reinicializações do servidor da web.
Michael K Madison
1
Michael, você precisa inverter o problema. Declare e codifique explicitamente as rotas que são exclusivamente para seu aplicativo (por exemplo, inscrição) com uma restrição de host ou subdomínio e, a seguir, trate suas rotas principais como "qualquer domínio ou subdomínio". É então responsabilidade de seus controladores pesquisar o domínio ou subdomínio atual e mapeá-lo para o cliente certo.
Justin francês de

Respostas:

95

Na verdade, é mais simples no Rails 3, conforme http://guides.rubyonrails.org/routing.html#advanced-constraints :

1) definir uma classe de restrição personalizada em lib/domain_constraint.rb:

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

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

2) use a classe em suas rotas com a nova sintaxe de bloco

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

root :to => 'main#index'

ou a sintaxe de opção antiquada

root :to => 'mydomain#index', :constraints => DomainConstraint.new('mydomain.com')
Leonid Shevtsov
fonte
6
Essa resposta parece muito mais simples para mim.
Jared
7
Esta é uma otima soluçao. Como funciona em um ambiente de desenvolvimento?
superluminário de
2
@superluminary funciona perfeitamente bem se você configurar domínios locais para desenvolvimento (por exemplo, via /etc/hosts).
Leonid Shevtsov,
7
Observação: se você usar o Pow localmente e tiver mydomain.com.dev, request.domainretornará .com.dev. Mude request.domainpara request.hoste funcionará perfeitamente.
Eric Muyser
2
Descobri que preciso criar rotas sem nome para que isso funcione, caso contrário, recebo o Invalid route name, already in use: 'root'erro ... Para fazer isso, alterei a rota pararoot :to => 'mydomain#index', as: nil
Just Lucky Really
5

No Rails 5, você pode simplesmente fazer isso em suas rotas:

constraints subdomain: 'blogs' do
  match '/' => 'blogs#show'
end
user3033467
fonte