Deviseを使ってユーザー認証機能を作ったときに、もうちょっとよくするための方法メモ。Deviseは普通の実装するとログイン後は/
にリダイレクトされる。それを別のURLにする方法はここに書いた。
では、ログインに必要なページのURLに直接アクセスがあったときはどうするか。流れとしてはこうなるのが望ましい。
- ログインが必要なページにアクセスがある
- ユーザーにログインフォームを見せる
- ログインが完了したら最初にアクセスしたURLへリダイレクト
ログインが必要かどうかはコントローラレベルで制御する。これはまあよくやるやつ。
# application_controller.rb
class ApplicationController < ActionController::Base
private
def sign_in_required
redirect_to new_user_session_url unless user_signed_in?
end
end
# hoge_controller.rb
class HogeController < ApplicationController
before_action :sign_in_required
def index
# ...
end
def ...
end
全てのコントローラで共有するために、ApplicationControllerにsign_in_required
というメソッドを定義する。これにはログインしていなければログインページヘリダイレクトする、と書かれている。このsign_in_required
は各コントローラのbefore_action
で使い、指定したアクションを要ログインにできる。
ここまでの実装だと1と2までは実現できているが、最初に開こうとしたURLへのリダイレクトは実現されておらず、ユーザーとしては非常に面倒くさい。リダイレクト部分を実現するためには最初のURLを覚えておいて、ログイン後のURLを/
ではなく別のURLに差し替えてあげる必要がある。
そこでApplicationControllerをさらに拡張する。
class ApplicationController < ActionController::Base
before_action :store_location
private
def sign_in_required
redirect_to new_user_session_url unless user_signed_in?
end
def store_location
return unless request.get?
if (request.path != "/users/sign_in" &&
request.path != "/users/sign_up" &&
request.path != "/users/password/new" &&
request.path != "/users/sign_out" &&
!request.xhr?)
session[:previous_url] = request.fullpath
end
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
end
store_location
でURLを覚えておき、after_sign_in_path_for
で覚えておいたURLを返すということをしている。そしてstore_location
を全てのアクションの【前】に呼び出している。
詳しいことは↓に書かれているが、ここの例ではstore_location
をafter_filter
に指定、つまり各アクションの後に呼んでいる。今回の例ではアクション中にログインページにリダイレクトされてしまうため、覚えておくURLが適切なものではなくなってしまう。そこでbefore_action
を指定することでなんとか実現している。