井原(@ihara2525)です。
apartmentを使ってサブドメインでテナントを切り替える場合、存在しないテナントのサブドメインにアクセスするとApartment::TenantNotFound
が投げられるのですが、これはApplicationController
等のレベルではrescueできません。
というわけで、Rackのミドルウェアを追加してそこで吸収するのですが、apartmentでいうところのElevator
を継承して、Apartment::Elevators::Subdomain
を親とするミドルウェアをつくってみます。
lib/middlewares/rescued_tenant_elevator.rb
require 'apartment/elevators/subdomain' class RescuedTenantElevator < ::Apartment::Elevators::Subdomain def call(env) super rescue Apartment::TenantNotFound request = Rack::Request.new(env) Rails.logger.error "Apartment Tenant not found: #{subdomain(request.host)}" [302, { 'Content-Type' => 'text/hml', 'Location' => Rails.application.routes.url_helpers.root_url }, self] end # needed to work def each end end
こんな感じで、super
を呼び出してまずApartment::Elevators::Subdomain
に処理を行ってもらい、例外が発生した場合は上記の例だとトップページに飛ばす、みたいな対応をしています。
さらに、これのテストも書いたので、それも載せておきます。
spec/lib/middlewares/rescued_tenant_elevator_spec.rb
require 'rails_helper' RSpec.describe RescuedTenantElevator do include Rack::Test::Methods let(:test_app) { -> (env) { [200, env, 'app'] } } let(:app) { described_class.new(test_app) } context 'when the tenant is public' do it 'returns 200' do get('http://example.org') expect(last_response.status).to eq 200 end end context 'when the tenant exists' do it 'returns 200' do get("http://#{SUBDOMAIN}.example.org") expect(last_response.status).to eq 200 end end context 'when the tenant does not exist' do it 'redirects to service top page' do get('http://foo.example.org') expect(last_response.status).to eq 302 expect(last_response.header['Location']).to eq 'http://localhost:3000/' end end end
テナントがない(サブドメインがない)場合や、テナントがある場合は200を返しますが、テナントがない場合は上記のミドルウェアの実装に従って302を返し、所定のページにリダイレクトされることをテストしています。
何かのご参考になれば幸いです!