Rails: лучшая практика для охвата запросов на основе поддоменов? - PullRequest
9 голосов
/ 21 октября 2009

Я работаю над приложением Rails (в настоящее время 2.3.4), которое использует субдомены для изоляции сайтов независимых учетных записей. Чтобы было ясно, я имею в виду, что foo.mysite.com должен отображать содержимое учетной записи foo, а bar.mysite.com должен отображать содержимое бара.

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

Например, один из моих контроллеров выглядит примерно так:

@page = @global_organization.pages.find_by_id(params[:id])        

(Примечание @global_organization устанавливается в application_controller через subdomain-fu.) Когда то, что я бы предпочел, это что-то вроде:

@page = Page.find_by_id(params[:id])

, где находки модели страницы автоматически попадают в нужную организацию. Я попытался использовать директиву default_scope, как это: (в модели страницы)

class Page < ActiveRecord::Base
  default_scope :conditions => "organization_id = #{Thread.current[:organization]}"
  # yadda yadda
end

(Опять же, просто чтобы заметить, тот же application_controller устанавливает Thread.current [: organization] для идентификатора организации для глобального доступа.) Проблема этого подхода заключается в том, что область действия по умолчанию устанавливается в первом запросе и никогда не изменяется последующие запросы к разным поддоменам.

Три очевидных решения на данный момент:

1 Используйте отдельные vhosts для каждого субдомена и просто запускайте разные экземпляры приложения для каждого субдомена (используя mod_rails). Этот подход не масштабируется для этого приложения.

2 Используйте оригинальный подход контроллера выше. К сожалению, в приложении довольно много моделей, и многие из этих моделей - это несколько удаленных из организации соединений, поэтому такая запись быстро становится громоздкой. Хуже всего то, что для этого активно требуется, чтобы разработчики помнили и применили ограничение или рискнули серьезной проблемой безопасности.

3 Используйте before_filter для сброса диапазона моделей по умолчанию для каждого запроса. Не уверен насчет производительности здесь или как лучше выбрать, какие модели обновлять по запросу.

Мысли? Любые другие решения, которые я пропускаю? Казалось бы, это достаточно распространенная проблема, так что должна быть лучшая практика. Все отзывы приветствуются, спасибо!

Ответы [ 5 ]

2 голосов
/ 22 октября 2009

Будьте осторожны при использовании области действия по умолчанию, так как это приведет вас к ложному чувству безопасности, особенно при создании записей.

Я всегда использовал ваш первый пример, чтобы прояснить ситуацию:

@page = @go.pages.find(params[:id])

Основная причина в том, что вы также хотите, чтобы эта связь применялась к новым записям, поэтому ваши действия new / create будут выглядеть следующим образом, гарантируя, что они правильно распределены по родительской ассоциации:

# New
@page = @go.pages.new

# Create
@page = @go.pages.create(params[:page])
2 голосов
/ 22 октября 2009

Вы пытались определить default_scope a lambda? Бит lambda, который определяет параметры, оценивается каждый раз, когда используется область.

class Page < ActiveRecord::Base
  default_scope lambda do 
   {:conditions => "organization_id = #{Thread.current[:organization]}"}
  end
  # yadda yadda
end

По сути, это ваш третий вариант, работая в тандеме с магией до фильтрации. Но это немного более агрессивно, чем то, что включает каждую находку, используемую в модели Page.

Если вы хотите, чтобы это поведение было для всех моделей, вы можете добавить default_scope в ActiveRecord :: Base, но вы упомянули, что некоторые из них являются парой соединений. Поэтому, если вы пойдете по этому пути, вам придется переопределить области действия по умолчанию в этих моделях для адресации соединений.

0 голосов
/ 25 мая 2013

Я обнаружил, что act_as_tenant gem [ github ] отлично подходит для этой функции. Это делает обзор для вас с минимальными дополнительными усилиями, а также затрудняет обход объема.

Вот первая запись в блоге об этом: http://www.rollcallapp.com/blog/2011/10/03/adding-multi-tenancy-to-your-rails-app-acts-as-tenant

0 голосов
/ 09 января 2012

Мы использовали https://github.com/penguincoder/acts_as_restricted_subdomain в течение последних 2 лет, но он работает только с Rails 2.3.

В настоящее время мы пытаемся обновить (и геммифицировать) плагин для работы с Rails 3. Мне интересно, как вы решили свою проблему.

0 голосов
/ 29 октября 2009

Возможно, вам лучше иметь базу данных для каждой учетной записи и переключать соединение базы данных в зависимости от субдомена.

В дополнение к вышеуказанной ссылке, если у вас есть модель (в вашем случае - Учетная запись), для которой вы хотите использовать базу данных по умолчанию, просто включите establish connection в модель.

class Account < ActiveRecord::Base

  # Always use shared database
  establish_connection  "shared_#{RAILS_ENV}".to_sym
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...