やりたいこと
メソッド間で認証情報を受け渡ししたい。
pass_authentication
メソッドで作った認証情報をconvert
メソッドに渡したい。
問題
pass_authentication
メソッドの中でredirect to
で遷移させているのに遷移されない。
ページを移動せずにそのまま次のconvert
メソッドが実行され、そこでエラーになっている。
認証ページに遷移して認証情報が取得できていないのでエラーになっている。
def index
pass_authentication
convert(@drive)
end
def pass_authentication
if request.params['code'] == nil # 認証コードを持っていなかった場合
auth_uri = auth_client.authorization_uri.to_s
redirect_to auth_uri, allow_other_host: true
else
# 省略
end
end
解決法
def index
pass_authentication
return if performed?
convert(@drive)
end
performed?
メソッドは、すでにredirect to
やrender
が呼び出されたかどうかを返す。
perfomed?メソッドは、すでにそのアクション内でrenderかredirectがされたかを確認するメソッドです
現場から学ぶ、RailsのControllerアンチパターン
今回はpass_authentication
メソッドの中でredirect to
を呼び出しているので、true
を返し、そこで処理が終了し、convert
メソッドは実行されない。
convert
メソッドを実行しないので、pass_authentication
メソッドにとどまったままになり、時間をかけてでもredirect to
が実行されるまで待つことになる。
なぜperformed?を使うのか?
performed?
メソッドを使わなくても以下のように実行されるのではないかと思った。
-
pass_authentication
メソッドが呼び出される -
redirect to
が実行される - ページが遷移する
- 認証が行われて元のページに戻る
-
convert
メソッドが実行される
ChatGPTに聞いたところ、2のところでredirect to
と書いたから(メソッドを呼び出したから)と言ってすぐに実行されるわけではなく、続けてその次の処理が行われることもあるという。
なぜかというと、redirect to
メソッドは処理を中断するものではないから。明示的に中断する処理を書かないと続く。
また、redirect to
が実行されたあとでまた別のredirect to
を実行しようとすると、AbstractController::DoubleRenderError
というエラーになるらしい。
今回の場合でいうと、2で認証ページに遷移させたあと、認証ページから戻ってくることになっているから、2回リダイレクトしていることになるのかも。
というわけで、まとめると以下2つが理由だ。
- 確実に次の処理が行われないようにするため
- 二重リダイレクトエラーを防ぐため
ドキュメント