0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

flashについて

Last updated at Posted at 2021-08-29

なぜ書くのか?

flashで表示すべきか?htmlで表示するべきか?を判断するときに、それぞれの技術の性質や役割を理解していないために、どちらを使えば良いのかを判断ができなかった。またそれらの技術の特徴を理解する以前に、基本的なこと(ブラウザのリクエスト、レスポンス、セッション(Cookie))についての理解が曖昧なために、どれだけ説明をよんでもちゃんと理解できなかった。そのため、本記事では研修で学んだ基礎的なことの復習をし、flashの技術の性質を理解するためにかく。

基本的なことのおさらい

HTTPとは

HTTPとはWebサーバとWebブラウザの間で情報のやり取りをするための通信規則です。(プロトコル) 例えばgoogleでサイトを閲覧するときも、このプロトコルに則ってブラウザがサーバーにリクエストし、その結果サイトを閲覧することができている。

HTTPの特徴として、常にブラウザ側からサーバーをよびだし、1つの要求(リクエスト)には1つの応答(レスポンス)を返すこと。そして、以前に何を要求したかで応答が変わることなく、同じ条件ならば、ある要求に対する応答は常に同じものになることです。(ステートレス)

似た名前でHTTPSがあります。HTTPとHTTPSの違いは通信が暗号化されていないか暗号化されているかの違いで、HTTPSが暗号化されています。( 僕の肌感ですが、だいたいのWebサイトがhttpsになっている気がします。

HTTPリクエスト

スクリーンショット 2021-07-18 21.13.54.png

HTTPメソッドを使ってサーバーに対して、必要なレスポンスの要求をすることです。HTTPリクエストには「HTTPリクエスト行」「HTTPヘッダー」「データ本体」の3つのパートがあります。

HTTPリクエスト行

HTTPリクエストの1行目が、「メソッド」「URL」「HTTPのバージョン」の3つの情報が含まれています。

HTTPヘッダー

主に5つが含まれている

  1. ユーザーエージェント名(User-Agent)
  • ブラウザの種類やOSの情報

    2.リファラ(Referer)

  • リクエストの発生元のページ名

  1. クッキー(Cookie)
    Cookieとは、ブラウザに対し特定の情報を保持させておく仕組みです。例えば、ログインしたことあるサイトであればIDとパスワードの情報を打たなくても、入力フォームに情報が入っているなど。

  2. 更新されていたら(If-Modified-Since)/同じでなければ(If-None-Match)

  • ブラウザに保存されているローカルキャッシュが変更されているかどうかを問い合わせています
  1. 受け取り希望情報(Accept、Accept-Language、Accept-Encoding、Accept-Charset)
  • どのようなデータを受け取りたいのか、言語、文字コード、画像の種類などの情報

データ本体

メソッドに「POST」が指定されている場合、送るデータが記載される
「GET」の場合は空になる

HTTPレスポンス

HTTPリクエストに従って、必要なレスポンスを返すこと。HTTPレスポンスには「レスポンス状態行」「HTTPヘッダー」「データ本体」の3つのパートがあります。

レスポンス状態行

  • HTTPステータスコードが含まれている

HTTPヘッダー

データ本体の各種の状態を示す情報が入れられている部分です。主に6つが含まれています。

  • コンテンツタイプ(Content-Type)
    どのようなデータを受け取ったのか?HTMLなのか画像、文字コードなどの情報。

  • 再利用期限(Expires)
    取得したデータを再度サーバーに問い合わせなくてもブラウザが再利用していい期限(キャッシュの制御に使われる)

  • データの最終更新日時(Last-Modified)やエンティティ情報(ETag)
    HTMLや画像などがいつ更新されたものかの情報や、サーバー上のファイルの場所ID、ファイルのサイズ、更新日時などから算出した、更新チェック情報。次回同じデータをリクエストする際に、これらの情報を使って更新されているかどうかを確認します。

  • キャッシュ制御(Cache-ControlやPragma)
    ブラウザや通信を橋渡しするプロキシが、データのキャッシュをどう扱うかの情報。

  • 接続状況(Connection)
    接続を持続するのか(keep-alive)、毎回接続を切断するのか(close)。ブラウザもサーバーもHTTPバージョン1.1の持続接続(keep-alive)を使える場合、通信のやりとりが効率良くなります。

  • 移動先(Location)
    リクエストと違う場所からデータを取得するように示す指示。新しい場所のURLが含まれます。いわゆるリダイレクト先を示す情報です。

本題(flashの技術の性質を理解していく)

flashって?

flashの性質を理解しようと、いくつかのサイトを参考にまとめた結果、下記のように動いていることが分かった。

リダイレクト前にセッションに値を保存して、リダイレクト後のページでセッションから値を取り出して、次のリクエストでセッションに保存した値を削除する

ここから2つの疑問がうまれる

  1. sessionにいつ保存したん?

    • flash[:key]= "hoge" としか書いてないのに、なぜcookieにflashの値が保存されているの?
    • ブラウザでcookieの中を探しても、hogeらしき文言が見つからない!
  2. sessionから、値を取得するための処理をコントローラ内に書かないと!?

    • cookieに保存された値を取得するためのメソッドはどこに書かれているのか?
    • 外部情報の取得 == GETリクエスト (間違った解釈) のはずだから、検証ツールのNetworkを確認すれば、GETのレスポンスの中にhogeがいるのではないか!

スクリーンショット 2021-07-18 21.09.27.png

疑問(勘違い)を抱いた背景

1の原因

  • railsチュートリアルでは、ログイン済みユーザーを返すためにに下記のコードを書いていた。

    なので、sessionと連携するために、session[:flash] = flashみたいなものを書くべき!と考えた。

# sessionに保存
def log_in(user)
    session[:user_id] = user.id
end 
  • flashの中の実装がどうなっているかを知らなかった

  • cookieに保存するときに、値が暗号化されることを忘れてしまっていた

2の原因

外部情報の取得 == GETリクエスト (間違った解釈)

  • HTTPリクエストをちゃんと理解ができていなかった
  • railsとsessionとの関係が理解できていない

下記では、flashの確認の前に、sessionの実体を探っていく

Sessionの仕組みについて知る

そもそもsession[:key] = "hoge"も、どうやって実装しているんだっけ?を紐解いていく
スクリーンショット 2021-07-18 21.10.34

ApplicationControllerから、順に継承元を見ていくと ActionController::Metal というクラスに、初めてsessioの文字を発見(githubリンク)

module ActionController
  ...

  class Metal < AbstractController::Base
    ...

    delegate :session, to: "@_request"

    ...
  end

  ...
end

ここから、ActionController::Metal sessionのメソッドを持っていることがわかるが、中身が@_requestで何を示していいるのかがわからない。。

inspectメソッドでクラス名を調べる

アクションの中に、raise session.inspectを挿入にしてオブジェクトを調べてみる

 def show
    raise session.inspect
    @task = Task.find(params[:id])
 end

結果、sessionActionDispatch::Request::Sessionであるとことが判明!

どのクラスかがわかったので、おってみる(gituhbリンク)

module ActionDispatch
  class Request
    ...

    class Session
      ...

      def [](key)
          @delegate[key]
      end
      ...
      def []=(k, v)
        @delegate[k] = v
      end
      ...
    end
  end
end

これでようやくアプリケーションの controllerで、sesion[:hoge]= "hogehoeg" のメソッドが使える理由が判明!

(継承クラスを追ってみて思った。結構複雑。日々何気なく、使ってるメソッドも先人の努力の賜物であり、先人に感謝しないとなと思った。)

セッションの管理

  • Railsでは、セッションの管理にCookieStoreがデフォルトで用意されている

    • cookieの他、Redis(インメモリ方式)などがある
  • Session情報を全てsecret_key_baseで暗号化し、クライアントのCookieに保存する

  • リクエストの際に、cookieに保存した暗号かされた情報から、アプリケーション内でsecret_key_baseで復号し、Session情報を取得する

flashの実装の中身を見ていく

sessionの仕組みがある程度わかったので、flashの実体を追っていく

  def show
    raise flash.inspect
    @task = Task.find(params[:id])
  end

 flashは、ActionDispatch::Flash::FlashHashであることがわかった。この中身を追っていく(github)

module ActionDispatch
   class Flash
    ...

    class FlashHash
      ...

      def [](key)
          @delegate[key]
      end
      ...

      def []=(k, v)
           @delegate[k] = v
      end
      ...
    end
  end
end

なるほど!これで、アプリケーションの controller、flash[:hoge]= "hogehoeg" のメソッドが使える理由がわかりました。

ここにflash.nowの実装も書かれていました。

flashをsessionに保存 or 削除

def commit_flash # :nodoc:
  session    = self.session || {}
  flash_hash = self.flash_hash

  # flash_hashをsessionに保存
  if flash_hash && (flash_hash.present? || session.key?("flash"))
    session["flash"] = flash_hash.to_session_value
    self.flash = flash_hash.dup
  end
  
  
  # sessionに保存していたflashをkeyとするhashを削除
  if session.loaded? && session.key?("flash") && session["flash"].nil?
     session.delete("flash")
  end
end

このcommit_flashActionDispatch::Routing::Redirectionの中で、読み込まれており、リダイレクトのたびに実行される。

4つのポイントに分けて確認する

 スクリーンショット 2021-07-18 21.10.34.png

1. flashメッセージをセッションに登録

  • コントローラ内のupdateアクションで、flashを作成
  • ここでは、sessionには保存されていない
def update
    ...
    flash[:success] = "タスク「#{@task.name}」を更新しました"
    binding.pry
   	...
end

pry(#<TasksController>)> flash
=> #<ActionDispatch::Flash::FlashHash:0x00007f980022c028
 @discard=#<Set: {}>,
 @flashes={"success"=>"タスク「変更」を更新しました"},
 @now=nil>
    
pry(#<TasksController>)> session[:flash]
=> nil

2. ルーティング後のコントローラ

  • リダイレクトのタイミングで、commit_flashメソッドが実行され、sessionflashを保存
def index
    binding.pry
    @tasks = Task.all
  	...
 end  
pry(#<#<Class:0x00007f95019d9228>>)> session[:flash]
=> {"discard"=>[], "flashes"=>{"success"=>"タスク「変更」を更新しました"}}

3. Viewでflashを表示

  • sessionに保存していたflash_hashの情報を取得し、画面に表示
<% binding.pry %>
<% flash.each do |key, value| %>
   <%= content_tag(:p, value, class: "mb-4 alert alert-#{key}") %>
<% end %>

pry(#<#<Class:0x00007fcb0feecd20>>)> flash
=> #<ActionDispatch::Flash::FlashHash:0x00007fcb1232e3f0
 @discard=#<Set: {"success"}>,
 @flashes={"success"=>"タスク「変更」を更新しました"},
 @now=nil>

4. 2回目のルーティング後のコントローラ

  • リダイレクトのタイミングで、commit_flashが実行され、sessionのflashが削除
  • flashの中身もすべて削除されている
def show
  binding.pry
  @task = Task.find(params[:id])
end

pry(#<TasksController>)> session[:flash]
=> nil
  
pry(#<TasksController>)> flash
=> #<ActionDispatch::Flash::FlashHash:0x00007fcb194be380
 @discard=#<Set: {}>,
 @flashes={},
 @now=nil>

この結果から、もちろんだが次のviewではflashメッセージは表示されていません。

flashとhtmlの使い分け

  • flash

    • アクションの結果をリダイレクト先で一時的に表示するときに使う
  • html

    • アクションの結果を現在のページで表示したい時(永続的)に使う

結果

研修でなんとなくで読んでいた箇所を今回改めて読むと以前よりも、理解できる箇所やイメージがつく箇所が増えた。正直なんとなくの理解でも、コードは動くことは動く。ただ、なんとなくの理解では、模倣でしかコードを書くことができず、応用がきかなくなりすぐに頭打ちがきそう。数年後の自分のためにも、納得して進めていきたいと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?