Help us understand the problem. What is going on with this article?

【初心者】Rails Sessionについて

はじめに

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アクションで受け取りユーザーをデータベースから見つけて検証します。

sessions_controller.rb
  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_usernilではない状態を指します。

# ユーザーがログインしていれば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
bd904197
現在公務員として働いています。27歳です。 railsエンジニアを目指し、プログラミング学習に励んでいます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away