はじめに
Railsチュートリアルの第8章を終えたのでアウトプットとして、Sessionについて投稿します。
#ステートレスとステートフルの違い
インターネットを支えているプロトコルであるHTTP
は、ステートレス
です。文字通り「状態(state)」がない(less)ので、HTTPリクエスト
の1つ1つは、それより前のリクエストの情報を引き継いでいません。
以下の例を見るとわかりやすい。
「ステートフルの例」
客: こんにちは
店員: いらっしゃいませ。○○バーガーへようこそ
客: ハンバーガーセットをお願いします
店員: サイドメニューは何になさいますか?
客: ポテトで
店員: ドリンクは何になさいますか?
客: ジンジャーエールで
店員: +50円でドリンクをLサイズにできますがいかがですか?
客: Mでいいです
店員: 以上でよろしいですか?
客: はい
店員: かしこまりました
「ステートレスの例」
客: こんにちは
店員: いらっしゃいませ。○○バーガーへようこそ
客: ハンバーガーセットをお願いします
店員: サイドメニューは何になさいますか?
客: ハンバーガーセットをポテトでお願いします
店員: ドリンクは何になさいますか?
客: ハンバーガーセットをポテトとジンジャーエールでお願いします
店員: +50円でドリンクをLサイズにできますがいかがですか?
客: ハンバーガーセットをポテトとジンジャーエール(M)でお願いします
店員: 以上でよろしいですか?
客: ハンバーガーセットをポテトとジンジャーエール(M)でお願いします。以上
店員: かしこまりました
ステートフル
の例では、店員(サーバ)が客(クライアント)の注文状態を覚えている(前の情報を引き継いでいる)。これをアプリケーション状態、あるいはセッション状態と呼ぶ。
これに対し、ステートレス
は、注文状態を覚えていません(前の情報を引き継いでいない)ということが分かります。ステートレス
な場合困る場合があります。それは、ログイン認証
やお買い物かご
などの機能を使用する場合です。前の通信を忘れられてしまったら、毎回「ログイン認証する」や「欲しい商品が増えるたび過去に入れた商品も含めて全部を籠に入れ直す」などの処理を行う必要があります。
セッションとは?
session
とは、ログインしたあと、ページ遷移しても再度ログインを行わなくても良いように、「ログインしている」ことを記憶しておく機能のこと。つまり、ステートフルな通信を実現するための仕組みである。
ログイン機能を実装する
コントローラーとルーティングの作成は省き、ログインフォームの作成から説明します。
セッションフォームとユーザー登録フォームの最大の違いは、セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない点です。したがって、新しいセッションフォームを作成するときにform_withヘルパー
に渡さなければならない情報は、若干異なります。
form_with(model: @user, local: true)
Railsでは上のように書くだけで、「フォームのactionは/usersというURLへのPOSTである」と自動的に判定しますが、セッションの場合はリソースのスコープ(ここではセッション)とそれに対応するURLを具体的に指定する必要があります。
form_with(url: login_path, scope: :session, local: true)
scope: :session
=> フォームデータが:sessionに入ることに注意。
次に、ログインフォームに入力された内容をセッションコントローラーのcreateアクションで受け取りユーザーをデータベースから見つけて検証します。
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# ユーザーログイン後にユーザー情報のページにリダイレクトする
else
# エラーメッセージを作成する
render 'new'
end
end
user = User.find_by(email: params[:session][:email].downcase)
①ログインフォームに入力されたEメールアドレスをparamsで受け取り、find_byでデータベースからユーザー情報を取り出し、downcaseメソッド
を使って、有効なメールアドレスが入力されたときに確実にマッチするようにする。
if user && user.authenticate(params[:session][:password])
②userがnil出ないかを調べかつ、authenticateメソッド
で認証します。
-
`authenticateメソッド`は、
- 誤ったパスワードの場合 → falseを返す
- 正しいパスワードの場合 → そのユーザーを返す
log_inメソッド
session[:user_id] = user.id
上のコードを実行すると、sessionメソッドでブラウザに保存し、変数のようにブラウザにアクセスできるようになる。
# 渡されたユーザーでログインする
def log_in(user)
session[:user_id] = user.id
end
current_user(現在ログイン中のユーザー)
ログインしているんだったら、current_user
を使ってそのユーザーの情報を返すようにします。また、if文
を使い、データベースの問い合わせ数を減らします。
# 現在ログイン中のユーザーを返す(いる場合)
def current_user
if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
end
end
レイアウトリンクを変更する
次に、ユーザーがログインしている時とそうでない時でレイアウトを変更します。そのために、ログインしているかしていないかの論理値を返すメソッドが必要なので、それを実装していきます。
ユーザーがログイン中の状態とは「sessionにユーザーidが存在している」こと、つまりcurrent_user
がnil
ではない状態を指します。
# ユーザーがログインしていればtrue、その他ならfalseを返す
def logged_in?
!current_user.nil?
end
論理値を返すlogged_in?
メソッドを使い次のようにレイアウトを変更します。
<% if logged_in? %>
# ログインユーザー用のリンク
<% else %>
# ログインしていないユーザー用のリンク
<% end %>
ログアウト
ログアウトするには、セッションからユーザーIDを削除します。
# 現在のユーザーをログアウトする
def log_out
session.delete(:user_id)
@current_user = nil
end