8章でやる事 →ユーザーがログインやログアウトを出来る様にする
ここでいうログインの基本的な仕組みとは、ブラウザがログインしている状態を保持し、ユーザーによってブラウザが閉じられたら状態を破棄するといった仕組み (認証システム (Authentification System))です。この認証システムの基盤が出来上がったら、ログイン済みのユーザー (current user) だけがアクセスできるページや、扱える機能などを制御していきます。
※適宜日本語化しているので本文とコードが違う部分があります
8.1 セッション
HTTPのリクエスト1つ1つは独立した処理の単位で、それより前のリクエストの情報は利用できない →あるページから別のページに移動した時にユーザー情報を保持する手段がHTTPプロトコル内には無い →sessionと呼ばれる接続を別途設定する
cookie
cookie→セッションを実装する一般的な方法 →ユーザーのブラウザに保存される小さなテキストデータ
ログインの仕組みの大半は、cookiesを使った認証メカニズムによって構築されています。
例によって作業用ブランチを作ってから開始ー!
$ git checkout -b basic-login
8.1.1 Sessionsコントローラ
ログインとログアウトの要素を、Sessionsコントローラの特定のRESTアクションにそれぞれ対応付けることにします。ログインのフォームは、この節で扱うnewアクションで処理します。createアクションにPOSTリクエストを送信すると、実際にログインします (8.2)。destroyアクションにDELETEリクエストを送信すると、ログアウトします (8.3) (表 7.1のHTTPメソッドとRESTアクションの関連付けを思い出しましょう)。
rails generate controller Sessions new
Rails gでアクションを生成すると、それに対応するビューも生成されるので 対応するビューが必要ないcreateやdestroyはコマンドで指定していない
routes.rb
SessionリソースではフルセットのRESTfulなルーティングは必要ない →「名前付きルーティング」だけを使用
Rails.application.routes.draw do #自動的に追加されているこの行を削除 get 'sessions/new' root 'static_pages#home' get '/help', to:'static_pages#help' get '/about', to:'static_pages#about' get '/contact', to:'static_pages#contact' get '/signup', to:'users#new' #本文では消えてるけど消すとエラーになるよ! post '/signup', to: 'users#create' #sessionsリソースの名前付きルートを追加 get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' resources :users end
require 'test_helper'
class SessionsControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
#名前付きルートの修正
get login_path
assert_response :success
end
end
追加した全ルーティングを表示
$ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / static_pages#home
help GET /help(.:format) static_pages#help
about GET /about(.:format) static_pages#about
contact GET /contact(.:format) static_pages#contact
signup GET /signup(.:format) users#new
POST /signup(.:format) users#create
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
まだよくわからないのも(PATCHとかPUTとか)あるけど多分後でやる
演習
- GET login_pathとPOST login_pathとの違いを説明できますか? 少し考えてみましょう。
- ターミナルのパイプ機能を使ってrails routesの実行結果とgrepコマンドを繋ぐことで、Usersリソースに関するルーティングだけを表示させることができます。同様にして、Sessionsリソースに関する結果だけを表示させてみましょう。現在、いくつのSessionsリソースがあるでしょうか? ヒント: パイプやgrepの使い方が分からない場合は Learn Enough Command Line to Be Dangerousの Section on Grep (英語) を参考にしてみてください。
#1. GET login_path → getのリクエスト → newアクションを呼び出す → viewの表示 POST login_path → postのリクエスト → createアクションを呼び出す → ログイン
#2. 本文の参考ページが英語過ぎて分からなかった🐫 [blogcard url=”https://eng-entrance.com/linux-command-grep#grepand”]
$ rails routes | grep users
signup GET /signup(.:format) users#new
POST /signup(.:format) users#create
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
$ rails routes | grep sessions
#以下の3つ
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#d
8.1.2 ログインフォーム
セッションはActive Recordオブジェクトではないのでエラーメッセージの自動生成はされない →flashメッセージでエラーを表示させる
Active Recordオブジェクトとはなんぞ
Active Recordを継承しているオブジェクト Active Recordとは→ データオブジェクトの作成/保存/検索のためのメソッドを持つライブラリ modelとDBの間でデータのやり取りをしてくれるモノ的な? [blogcard url=”https://qiita.com/Ryokky/items/a1d0b4e86bacbd7ef6e8”]
セッションフォームとユーザー登録フォームの違い
セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない点です
#user登録フォームではインスタンス変数@userが使えた <%= form_for(@user) do |f| %> . . . <% end %> #フォームのactionは/usersというURLへのPOSTと、自動判別してもらえた form_for(@user) #sessionの場合はモデルが無い(インスタンス変数にあたるものもない)のでリソース名と対応URLを具体的に指定する form_for(:session, url: login_path)
form_forとform_tag
どちらもフォームを作ってくれるヘルパーだけど働きがかなり違う
form_forの代わりにform_tagを使うこともでき、Railsではこの方が慣用的な方法です。しかし、ユーザー登録フォームではform_forを使うのが一般的であり、並列構造を強調するためにもform_forを使いました。
参考 [blogcard url=”https://railsguides.jp/form_helpers.html#%E3%83%A2%E3%83%87%E3%83%AB%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E6%89%B1%E3%81%86”] [blogcard url=”https://qiita.com/chopesu_se/items/3749a4a657dbbba1258b”] ざっくり言うと モデルを通してテーブルに情報を追加・編集を行うフォーム→form_for 情報の追加・編集を行わないフォーム→form_tag この考えで言うとログインフォームは情報の追加・編集が無いからform_tagじゃね?って思うけど 登録フォーム(modelに基づく)と並列構造なので敢えてform_forと言う事っぽい
from_with
form_forとform_tagはRails5.1では非推奨 →form_withに置き換えられる [blogcard url=”https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2”]
ログインフォームのコード
ユーザー登録ページのコードを参考にログインフォームを作る
<% provide(:title, "Log in") %>Log in
<%= form_for(:session, url: login_path) do |f| %> <%= f.label :email, User.human_attribute_name(:email) %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password, User.human_attribute_name(:password) %> <%= f.password_field :password, class: 'form-control' %> <%= f.submit t('.login'), class: "btn btn-primary" %> <% end %><%= t('.new_user?') %><%= link_to t('.signup'), signup_path %>
・ ・ ・・ ・ ・
演習
リスト 8.4で定義したフォームで送信すると、Sessionsコントローラのcreateアクションに到達します。Railsはこれをどうやって実現しているでしょうか? 考えてみてください。ヒント:表 8.1とリスト 8.5の1行目に注目してください。