deviseのwikiにあるHow To: Change the default sign_in and sign_out routes を翻訳、実際に動かしてみる。
通常、deviseを実装した段階では、生成されるルーティングはdeviseがデフォルトで提供してくれるものになります。例えばuserモデルのログインなどであれば/users/sign_in
や/users/sign_out
です。
もちろんこのままでも機能的には問題なく動作しますし、画面表示上の違いは無いのですが、わかりやすいURL構造にすることによって検索エンジンに有利に働いたり、ユーザーにわかりやすくなったりするというメリットもあったりします。
そういった場合に、deviseで対応するルーティングを任意のものに変える方法を解説していきます。
言語、ライブラリのバージョン
導入から基本的な実装を知りたい場合は最小構成導入からログアウトまでを参照。 deviseを使用しているモデルはUserモデルとしています。適宜自分の環境に読み替えてください。
また、今回出てくるコードは全てroutes.rbに記述するものになります。
devise_scopeをカスタマイズする方法
はじめに紹介する方法はdevise_scopeを使ってURLと対応するアクションを設定する方法です。
Rails.application.routes.draw do devise_scope :user do get 'login', to: 'devise/sessions#new' end end
devise_scope :モデル名とすることでブロック内でカスタマイズしたルーティングの設定ができます。デバイスを使用しているモデルが複数ある場合は同じURLの構文は使えないのでnamespaceなどで切り分けましょう。
上記記述によって下記URLでloginページにアクセスできるようになります。
http://localhost:3000/login
また、devise_scopeはasとエイリアスされているので、下記構文も同様に動作します。
つまり、下記のように書いた場合、http://localhost:3000/login
でも、http://localhost:3000/login2
でもログインページに遷移することができます。
Rails.application.routes.draw do devise_scope :user do get 'login', to: 'devise/sessions#new' end as :user do get 'login2', to: 'devise/sessions#new' end end
sign_outの場合も同じように書けます。
Rails.application.routes.draw do devise_scope :user do delete 'logout', to: 'devise/sessions#destroy' end end
特定のルーティングをカスタマイズする
skipオプションを利用することで、特定のデフォルトルーティングをスキップし、独自のルーティングをカスタマイズできます。例えば、sessionsに関するルーティングだけを特定のurlにしたい場合は下記のようにします。
Rails.application.routes.draw do devise_for :users, skip: [:sessions] as :user do get 'signin', to: 'devise/sessions#new', as: :new_user_session post 'signin', to: 'devise/sessions#create', as: :user_session delete 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session end end
最初のdevise_for :users, skip: [:sessions]でsessionsコントローラーのみルーティングをスキップします。
resources :user except: [:edit]
のような形式と似ていますね。
その上でカスタマイズしたルーティングを設定できます。上記例では、
http://localhost:3000/signin
でログインページに遷移できます。
この方法でカスタマイズを行った場合、deviseが提供するhelperメソッドの結果としてリダイレクトなどが行われた場合、自動的にこのURLに遷移されるようになります。
例えば、authenticate_user!などのメソッドによって未ログイン時には遷移できないページにアクセスしようとした場合、ログインページに遷移することになりますが、その際のurlはhttp://localhost:3000/signin
になります。
sign_out_via
を設定している場合
この部分は本題から逸れてしまうので、必要なければ読み飛ばしてください。 ログアウト処理を行った場合、
No route matches [GET] "/users/sign_out"
のようなエラーに遭遇する場合があります。このエラーはdeviseのサインアウトのメソッドはdeleteになっているのに対して、getメソッドとしてアクセスしようとした場合におきます。
よくあるのはlink_to
のオプションとしてmethod: :deleteを使用しているのにそれが効かなかったりした場合ですね。この問題を解消するにはrails_ujs
などを入れることで動的にアンカーリンクを生成することで対応するケースが多いですが、deviseでも、
config/initializers/devise.rb
にあるconfig.sign_out_via
というオプションの値を:deleteから:getに変えてあげても対応できます。
Devise.setup do |config| #中略 config.sign_out_via = :get #中略 end
この対応を行った場合に、そのままだとデフォルトの挙動、つまりdeleteを見に行ってしまうので sign_out_viaを適用するように指定してあげる必要があります。
Rails.application.routes.draw do devise_for :users, skip: [:sessions] as :user do get 'signin', to: 'devise/sessions#new', as: :new_user_session post 'signin', to: 'devise/sessions#create', as: :user_session match 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session, via: Devise.mappings[:user].sign_out_via end end
簡単にカスタマイズしたい場合
上記ほどのカスタマイズはいらず、単純にsign_in
をlogin, sign_out
をlogoutにしたい場合、下記一行で簡単に変更することも可能です。
Rails.application.routes.draw do devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout'} end
この記述を行うことで、
http://localhost:3000/login
http://localhost:3000/logput
のURLでそれぞれの該当ページに遷移、処理を実行することができます。
以上、deviseのログイン/ログアウトのルーティングカスタマイズの方法でした。 実務では何かと要望の多いこの辺りの実装は何度も調べてしまいがちです。参考になれば幸いです。