1. はじめに
謎のエラーに苦しめられました。
結論から言うと原因は「routes.rb」への記載の順番でした。(なにそれ?って感じですよね)
Railsアプリを作っているけど、やっぱり中身が分かっていないものを使うのは難しい。ログイン機能を簡単に実装できるdevise gemはめちゃくちゃ便利だけど、4時間解決出来なかったエラーの原因がroutes.rbへの記載の順番だとは思いもしなかった。難しいなぁ〜
— ゆうすけ@プログラミング (@ysk_pro) 2018年2月18日
(失敗ナレッジの共有をして欲しいとのご要望をいただいたので書いてみます。ありがとうございます。)
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
発生したエラー
(言われた通り大人しく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" が一番上になかったために引っかからず、正しいルーティングを選択できたからでした。
ルーティングも奥が深いですね。
まだ知らないことばっかりで楽しいな〜(エラー最中はイライラが止まらないけど、解決して分析してスッキリするの好き)
ご指摘・ご感想等いただけますと大変嬉しいです!
12. 追記
このエラーをなんとか解決して、初めてのwebサービスを作ることができたので、ぜひこちらも合わせてご覧ください!
ysk-pro.hatenablog.com
また、僕が通っていたプログラミングスクール / DIVE INTO CODEに通った感想・できるようになったことをまとめましたのでご興味あれば見てみてください。
ysk-pro.hatenablog.com