目的は、サービス内のアカウントごとにサブドメインを利用できるようにすること。
Shopというリソースはdomain_nameを持ち、http://example.com/:domain_name
がshops#show
にルーティングされるようにしている。これをhttp://domain_name.example.com/
でshops#show
にアクセスできるようにする。
参考
- Ruby On Rails でサブドメインを使った Web サイトを作る
- Railsで環境毎にサブドメインとパスを切り替える
- Railsでカスタムサブドメイン機能を実装する時のルーティング設定
- Rails3でサブドメインを利用したルーティングを設定する
はじめに
- 開発環境でサブドメインの検証をするには
lvh.me
を使う-
http://lvh.me:3000
でローカルホストにアクセスできる
-
ルーティング設定
lib/subdomain.rbの作成
このクラスをルーティングに用いる。
class Subdomain
def self.matches?(request)
# wwwの場合は追加するルーティングにマッチングしないようにしておく
request.subdomain.present? && request.subdomain != 'www'
end
end
config/routes.rbの設定
shopsリソースについて、Shopモデルはdomain_nameという項目を持ち、showアクションの際に、
domain_nameをキーにして取得している。
# config/routes.rb
require Rails.root.join('lib', 'subdomain.rb')
constraints Subdomain do # サブドメインが指定されていた場合に以下のルーティングが利用される
get 'shop', to: 'shops#show', path: '/' # サブドメインごとのルートパスという感じ?
end
# ちなみにサブドメイン導入前にのルーティング(残している)
get 'domain_shop', to: 'shops#show', path: ':domain_name', constraints: { format: /()/ }
コントローラ側
constraints Subdomain do
内でルーティングされた際には、request.subdomain
が取得できる。
なので、shops#showでshopインスタンスを取得する処理を以下のように変更する。
def set_shop_by_domain_name
if request.subdomain.blank?
@shop = Shop.find_by!(domain_name: params[:domain_name])
else
@shop = Shop.find_by!(domain_name: request.subdomain)
end
rescue
redirect_to root_url(subdomain: false), notice: 'error message'
# サブドメインが指定されたがShopにレコードがない場合にリダイレクトループになるので、
# sudbomain: falseでサブドメインを除外しておく。
end
Cookieをサブドメインで共有する
注意点は、参考リンクなどにもあるようにセッションの削除ができなくなるケースに陥らないこと。
セッションストアの設定は、環境ごとに分ける記事がいくつかあったけれど、以下の設定でいけた。
# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store, key: key_name, domain: :all
サブドメインとメインドメイン間のルーティング
このままでは、サブドメインでルーティングされた後にroot_urlがsudbomain.example.jp
という風に固定されてしまう。
link_to
なりでリンクを作成する際に、以下のようにするとサブドメインをクリアできるみたい。
link_to 'link_name', root_url(subdomain: false)
CORS?
以下の様なエラーが発生。
ActionController::InvalidCrossOriginRequest - Security warning: an embedded <script> tag on another site requested protected JavaScript. If you know what youre doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.:
サブドメインから、メインドメインへ移るためにルーティングで以下のように設定していた。
constraints Subdomain do
get 'shop', to: 'shops#show', path: '/'
get '*a' => redirect(subdomain: false) #, constraints: { format: :html }
# この部分でサブドメインからメインドメインにリダイレクトしていて、そのJSがエラーになっていた。
end
メインドメインへ移るリンクはroot_url(subdomain: false)
を使うように決めた。
関係ないけれど
最初は、CORSについて調べていたがAPIに対する記述ばかりみていた。意味がわからなかった。gemのrack-cors
を使ったりしていたが、gemに問題があるような記事があったりして、良くわからなかった。せっかく調べたので設定箇所などをメモしておく。
- How to avoid ActionController::InvalidCrossOriginRequest exception?
- ActionController::InvalidCrossOriginRequest exception due to bingbots
- Wildcard CORS in RAILS
- CORS Headers With Devise
- CORS(Cross-Origin Resource Sharing)について整理してみた
- sinatraでCORSに対応する
# conifg/application.rb
config.middleware.use Rack::Cors do
# 以下のようにする必要があるケースがあるらしい
#config.middleware.insert_before ActionDispatch::Static, Rack::Cors do
allow do
#origins '*'
origins 'http://*.lvh.me:3000', '127.0.0.1:3000', 'localhost:3000', /http:\/\/.*\.lvh\.me:(\d*)/
resource '*', headers: :any, :methods => [:get, :post, :delete, :put, :options]
end
end
けれど、responseのheaderに以下の様なヘッダーは入ってこなかった。コントローラをいじらなければならないのか?クライアントサイドの開発・APIの開発の際にもう一度調べることになりそう。
Access-Control-Allow-Methods →GET, POST, PUT, DELETE
Access-Control-Allow-Origin →*
ちなみに問題となっていたJSの処理は以下。
$('#id').val(this.value);
$.ajax({
type: 'GET',
url: '/controller_a/action'
});
Rendered controller_a/action.js.erb (89.7ms)
の際にリダイレクトされたのが問題だったということ?