概要
こんにちは。
現在Docker×Rails×MySQLでアプリを開発中です。
わざわざログイン画面に遷移しなくても、ユーザー登録が済んでいればトップページからでもメールやパスワードを入力してログインできるようなサイトをよく見かけます。
自分の開発しているアプリにもぜひ取り入れたいと思い実装をしたのですが、少し難儀したのでまとめたいと思います。
もっとも伝えたいことが、ビューのコピペだけではログインできなかったという点の解消です。
前提条件
・ユーザー管理機能にはdeviseを使用しています。
・ログイン画面にてログインができることを確認の上、別のページでもログインフォームを作るのが目的です。
・ログインフォームのみ。新規登録画面(サインアップ)は実装しません。
実装(ビューファイルのみ)
ログイン画面におけるビューはこのような形です。
<%= form_with model: @user, url: user_session_path, class: 'registration-main', local: true do |f| %>
<div class='form-wrap'>
<div class='form-header'>
<h1 class='form-header-text'>
ログイン
</h1>
</div>
<div class='login-flash-message'>
<%= flash[:notice] %>
<%= flash[:alert] %>
</div>
<div class="form-group">
<div class='form-text-wrap'>
<label class="form-text">メールアドレス</label>
<span class="indispensable">必須</span>
</div>
<%= f.email_field :email, class:"input-default", id:"email", placeholder:"PC・携帯どちらでも可", autofocus: true %>
</div>
<div class="form-group">
<div class='form-text-wrap'>
<label class="form-text">パスワード</label>
<span class="indispensable">必須</span>
</div>
<%= f.password_field :password, class:"input-default", id:"password", placeholder:"" %>
</div>
<div class='login-btn'>
<%= f.submit "ログイン" ,class:"login-red-btn" %>
</div>
<div class="info-text-signup">
会員登録がまだの人は<span><%= link_to "こちら", new_user_registration_path %></span>
</div>
</div>
<% end %>
参考画像
※CSSコーディングしています。
ここからemailとpasswordの入力フォーム及び、サインインボタンを切り取ります。
<% unless user_signed_in? %>
<%= form_with model: @user, url: user_session_path, class: 'top-session', local: true do |f| %>
<%= f.email_field :email, class:"top-input-signin", id:"email", placeholder:"email" %>
<%= f.password_field :password, class:"top-input-signin", id:"password", placeholder:"password" %>
<div class='top-signin-btn'>
<%= f.submit "sign in" ,class:"top-signin-white-btn" %>
</div>
<div class="top-info-text-signup">
会員登録がまだの人は<span><%= link_to "こちら", new_user_registration_path %></span>
</div>
<% end %>
<% end %>
参考画像
このフォームをトップページに設置することができました!!
# 問題点
これで登録されているemailとpasswordを入力してもサインインはできません。
入力後、sign inボタンを押しても、強制的にサインイン画面に遷移し、エラーメッセージが表示されます。
/users/sign_in の画面
ログのパラメーターをみてみましょう。
これが現時点でのトップページに設置したログイン画面よりログインした時に送られたパラメーターの詳細です。
web_1 | Started POST "/users/sign_in" for 172.28.0.1 at 2021-03-28 06:47:33 +0000
web_1 | Cannot render console from 172.28.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web_1 | Processing by Devise::SessionsController#create as HTML
web_1 | Parameters: {"authenticity_token"=>"[FILTERED]", "email"=>"test@test.com", "password"=>"[FILTERED]", "commit"=>"sign in"}
web_1 | Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms | Allocations: 502)
web_1 |
次にログイン画面より通常通りログインした時のログです。
web_1 | Started POST "/users/sign_in" for 172.28.0.1 at 2021-03-28 07:11:37 +0000
web_1 | Cannot render console from 172.28.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web_1 | Processing by Devise::SessionsController#create as HTML
web_1 | Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"test@test.com", "password"=>"[FILTERED]"}, "commit"=>"ログイン"}
web_1 | User Load (1.2ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'test@test.com' ORDER BY `users`.`id` ASC LIMIT 1
web_1 | Redirected to http://localhost:3000/
web_1 | Completed 302 Found in 229ms (ActiveRecord: 3.4ms | Allocations: 3620)
web_1 |
パラメーターの中身に注視すると、
うまくいく時のパラメーターが
web_1 | Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"test@test.com", "password"=>"[FILTERED]"}, "commit"=>"ログイン"}
に対して、
トップページからのログインのパラメーターは
web_1 | Parameters: {"authenticity_token"=>"[FILTERED]", "email"=>"test@test.com", "password"=>"[FILTERED]", "commit"=>"sign in"}
と、userというハッシュの中にまとめられていない**ことがわかります。
そのため入力された値がどのテーブルから探しているのかわからず、エラーとなったのだと考えます。
これの解消にやや難儀したのですが、基本的なことでした。
どちらもログインにあたって、Devise::SessionsController#createが働いていますが、
そもそもトップページで働くコントローラーはメインのコントローラーのindexアクションがほとんどです。
従いまして、コントローラーにuserのインスタンス変数を定義しなければなりませんでした。
実装(コントローラー)
class HomesController < ApplicationController
def index
@user = User.new
end
end
これで無事トップページからもログインができました。
まとめ
この実装のために色々と的外れなことを調べたり試行錯誤していました。
今一度基本に立ち戻り、コードを見直すことの重要性を痛感しました。
MVCモデルのRailsにおいて、
「今自分が実装しているところで動くコントローラーはなんだろう?」
「ルーティングはどの流れで動いているだろう?」
「このビューにおいて設定するインスタンス変数はどこで定義されているだろう?またすべきだろう?」
そんな常に疑問視しながらコードを書いていこうと感じました。
また、この度初めてTeratailにて質問を投じさせていただきました。
ご回答いただいたエンジニアさんには感謝です。
いつか自分が逆に他の人のエラーを助けれるようなエンジニアになるべく精進していきます。
ご覧いただきありがとうございました。