銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

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

1. はじめに

謎のエラーに苦しめられました。
結論から言うと原因は「routes.rb」への記載の順番でした。(なにそれ?って感じですよね)


(失敗ナレッジの共有をして欲しいとのご要望をいただいたので書いてみます。ありがとうございます。)

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

2. 事実

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

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

発生したエラー

f:id:ysk_pro:20180219222407p:plain
(言われた通り大人しく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)

これの無限ループ

3. 解決法

routes.rb

  devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks", registrations: 'registrations'}
  resources :users, only: [:show]

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

4. 分析

エラーしている状態の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)                   users#show

当然、

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" が一番上になかったために引っかからず、正しいルーティングを選択できたからでした。


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

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