Edited at

「localhost でリダイレクトが繰り返し行われました。」はルーティングのせいだった、Ruby on Rails エラーの分析

More than 1 year has passed since last update.

謎のエラーに苦しめられました。

結論から言うと原因は「routes.rb」への記載の順番でした。

個人ブログに同様の内容を書いておりましたが、技術的な内容はQiitaに集約することにしたので、こちらにも投稿します。

(参考)個人ブログ

※ バージョンは、Ruby:2.3.0、Rails:5.1.4です。


事象

本件に関係のある箇所を抜粋しています。


routes.rb

  resources :users, only: [:show]

devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks", registrations: 'registrations'}

devise gemを利用してログイン機能を実装しています。

ユーザー詳細ページをつけるために、resources :users, only: [:show] で users#show のためのルーティングを設定しています。


users_controller.rb

  before_action :sign_in_required, only: [:show]

def show
# 以下略


application_controller.rb

  private

def sign_in_required
redirect_to new_user_session_url unless user_signed_in?
end

発生したエラー



(言われた通り大人しくCookieを消しましたが効果ありませんでした)

ログ

Started GET "/users/sign_in" for 127.0.0.1 at 2018-02-19 21:58:38 +0900

Processing by UsersController#show as HTML
Parameters: {"id"=>"sign_in"}
Redirected to http://localhost:3000/users/sign_in
Filter chain halted as :sign_in_required rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)

Started GET "/users/sign_in" for 127.0.0.1 at 2018-02-19 21:58:38 +0900
Processing by UsersController#show as HTML
Parameters: {"id"=>"sign_in"}
Redirected to http://localhost:3000/users/sign_in
Filter chain halted as :sign_in_required rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)

これの無限ループ


解決法


routes.rb

  devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks", registrations: 'registrations'}

resources :users, only: [:show]

順番を入れ替えただけで直りました。マジか、、


分析

エラーしている状態のrails routesのログ

                           user GET      /users/:id(.:format)                   users#show

new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
user_password PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
POST /users/password(.:format) devise/passwords#create
cancel_user_registration GET /users/cancel(.:format) registrations#cancel
new_user_registration GET /users/sign_up(.:format) registrations#new
edit_user_registration GET /users/edit(.:format) registrations#edit
user_registration PATCH /users(.:format) registrations#update
PUT /users(.:format) registrations#update
DELETE /users(.:format) registrations#destroy
POST /users(.:format) registrations#create
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) omniauth_callbacks#passthru
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) omniauth_callbacks#twitter
new_user_confirmation GET /users/confirmation/new(.:format) devise/confirmations#new
user_confirmation GET /users/confirmation(.:format) devise/confirmations#show
POST /users/confirmation(.:format) devise/confirmations#create

エラー解決した状態のrails routesのログ

                         Prefix Verb     URI Pattern                            Controller#Action

new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
user_password PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
POST /users/password(.:format) devise/passwords#create
cancel_user_registration GET /users/cancel(.:format) registrations#cancel
new_user_registration GET /users/sign_up(.:format) registrations#new
edit_user_registration GET /users/edit(.:format) registrations#edit
user_registration PATCH /users(.:format) registrations#update
PUT /users(.:format) registrations#update
DELETE /users(.:format) registrations#destroy
POST /users(.:format) registrations#create
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) omniauth_callbacks#passthru
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) omniauth_callbacks#twitter
new_user_confirmation GET /users/confirmation/new(.:format) devise/confirmations#new
user_confirmation GET /users/confirmation(.:format) devise/confirmations#show
POST /users/confirmation(.:format) devise/confirmations#create
user GET /users/:id(.:format)

当然、

user GET      /users/:id(.:format)     users#show

の行の場所が変わるだけです。

ログをよく見てみると、

Started GET "/users/sign_in" for 127.0.0.1 at 2018-02-19 21:58:38 +0900

Processing by UsersController#show as HTML
Parameters: {"id"=>"sign_in"}
Redirected to http://localhost:3000/users/sign_in

GET "/users/sign_in" にアクセスしようとしてるけど、GET "/users/:id" に "id" = "sign_in" としてアクセスしちゃってる・・・

そして、 users#show はログインしていないとログインページに強制リダイレクトする設定になっているので、 GET "/users/sign_in" にアクセスして以下、無限ループです。

更に、routes.rbの記載の順番によって、エラー発生の有無が決まるのは、


Railsのルーティングは、ルーティングファイルの「上からの記載順に」マッチします。


のためでした。

(参考)Rails のルーティング | Rails ガイド

つまり、解決した時には、GET "/users/sign_in" にアクセスしようとした時に、GET "/users/:id" が一番上になかったために引っかからず、正しいルーティングを選択できたからでした。

ルーティングも奥が深いですね。

まだ知らないことばっかりで楽しいな〜(エラー最中はイライラが止まらないけど、解決して分析してスッキリするの好き)

ご指摘・ご感想等いただけますと大変嬉しいです!