Enrutamiento de rieles para manejar múltiples dominios en una sola aplicación

90

No he podido encontrar una solución viable a este problema, a pesar de varias preguntas similares aquí y en otros lugares. Parece probable que esta pregunta no haya sido respondida para Rails 3, así que aquí va:

Tengo una aplicación que actualmente permite a los usuarios crear su propio subdominio que contiene su instancia de la aplicación. Mientras que en Rails 2 te sirvió mejor con la gema subdominio-fu, en la versión 3 es dramáticamente más simple, según Railscast - http://railscasts.com/episodios/221-subdomains-in-rails-3 .

Eso es bueno, pero también quiero ofrecer la opción para que los usuarios asocien su propio nombre de dominio con su cuenta. Entonces, si bien pueden tener http://userx.mydomain.com , me gustaría que eligieran tener asociado http://userx.com también.

Encontré algunas referencias para hacer esto en Rails 2, pero esas técnicas ya no parecen funcionar (particularmente esta: https://feefighters.com/blog/hosting-multiple-domains-from-a-single-rails -app / ).

¿Alguien puede recomendar una forma de usar rutas para aceptar un dominio arbitrario y pasarlo a un controlador para que pueda mostrar el contenido apropiado?

Actualización : he obtenido la mayor parte de la respuesta ahora, gracias a la respuesta oportuna de Leonid y una nueva mirada al código. En última instancia, requirió una adición al código de subdominio existente que estaba usando (de la solución Railscast) y luego agregar un poco a route.rb. Aún no he llegado hasta allí, pero quiero publicar lo que tengo hasta ahora.

En 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

Agregué la segunda clase a imitación de la primera, que se sabe que funciona. Simplemente agrego una condición que garantiza que el dominio entrante no sea el que estoy alojando el sitio principal.

Esta clase se usa en route.rb:

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

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

Aquí, estoy anteponiendo el código de subdominio existente (nuevamente, funciona bien) con una estrofa para verificar el dominio. Si este servidor responde a ese dominio y no es el que opera el sitio principal, reenvíe al controlador especificado.

Y aunque eso parece estar funcionando, todavía no tengo todo funcionando, pero creo que este problema en particular se ha resuelto.

Aaron Vegh
fuente
1
Muchas gracias por tu edición, Aaron. Estoy lidiando exactamente con la misma situación en este momento. Como pregunta de seguimiento, ¿cómo logras que tu servidor acepte cualquier dominio que se le reenvíe? Supongo que sería una configuración en el archivo .conf, pero no estoy seguro de qué. ¡Cualquier ayuda sería apreciada!
Deadwards
Aaron, estoy contigo. Yo quiero hacer lo mismo. Pero no quiero codificar el dominio. Quiero que todo se haga mediante programación sin archivos de zona y reinicios del servidor web.
Michael K Madison
1
Michael, necesitas darle la vuelta al problema. Declare explícitamente y codifique las rutas que son exclusivamente para su aplicación (por ejemplo, registro) con una restricción de host o subdominio, luego trate sus rutas principales como "cualquier dominio o subdominio". Entonces, es responsabilidad de sus controladores buscar el dominio o subdominio actual y asignarlo al cliente correcto.
Justin French

Respuestas:

95

En realidad, es más simple en Rails 3, según http://guides.rubyonrails.org/routing.html#advanced-constraints :

1) defina una clase de restricción personalizada en lib/domain_constraint.rb:

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

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

2) usa la clase en tus rutas con la nueva sintaxis de bloque

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

root :to => 'main#index'

o la sintaxis de opciones anticuada

root :to => 'mydomain#index', :constraints => DomainConstraint.new('mydomain.com')
Leonid Shevtsov
fuente
6
Esta respuesta me parece mucho más sencilla.
Jared
7
Esta es una gran solucion. ¿Cómo funciona con un entorno de desarrollo?
superluminario
2
@superluminary funciona perfectamente bien si configura dominios locales para el desarrollo (por ejemplo, via /etc/hosts).
Leonid Shevtsov
7
Nota: si usa Pow localmente y tiene midominio.com.dev, request.domaindevuelve .com.dev. Cambie request.domaina request.hosty funciona perfectamente.
Eric Muyser
2
Descubrí que tengo que crear rutas sin nombre para que esto funcione, de lo contrario aparece el Invalid route name, already in use: 'root'error ... Para hacer esto, cambié la ruta aroot :to => 'mydomain#index', as: nil
Just Lucky Really
6

En Rails 5, simplemente puede hacer esto en sus rutas:

constraints subdomain: 'blogs' do
  match '/' => 'blogs#show'
end
usuario3033467
fuente