flashについて

# なぜ書くのか？
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611089/ae3c3abc-8e04-15aa-847f-110ceae961e9.png)



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

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

### HTTPヘッダー

主に5つが含まれている

1. ユーザーエージェント名（User-Agent）

- ブラウザの種類やOSの情報

  2.リファラ（Referer）

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

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

4. 更新されていたら（If-Modified-Since）／同じでなければ（If-None-Match）
- ブラウザに保存されているローカルキャッシュが変更されているかどうかを問い合わせています

5. 受け取り希望情報（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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611089/046f7c1f-94e0-569f-750a-9d2321a0a175.png)




## 疑問（勘違い）を抱いた背景


### 1の原因

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

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

```ruby
# sessionに保存
def log_in(user)
    session[:user_id] = user.id
end 
```

- flashの中の実装がどうなっているかを知らなかった

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



### 2の原因

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

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

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

# Sessionの仕組みについて知る

そもそも`session[:key] = "hoge"`も、どうやって実装しているんだっけ？を紐解いていく
<img title='スクリーンショット 2021-07-18 21.10.34.png' alt='スクリーンショット 2021-07-18 21.10.34' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611089/4232066b-7d73-e8ad-0a25-3ce87e97278f.png' width="380" data-meta='{"width":1280,"height":508}'>


`ApplicationControlle`rから、順に継承元を見ていくと `ActionController::Metal `というクラスに、初めて`sessio`の文字を発見([githubリンク](https://github.com/rails/rails/blob/4a906cbbbc68f41ab23d2510954336db63451acc/actionpack/lib/action_controller/metal.rb#L208))

```ruby
module ActionController
  ...

  class Metal < AbstractController::Base
    ...

    delegate :session, to: "@_request"

    ...
  end

  ...
end
```



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



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

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

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



結果、`session`が`ActionDispatch::Request::Session`であるとことが判明！

どのクラスかがわかったので、おってみる（[gituhbリンク](https://github.com/rails/rails/blob/4a906cbbbc68f41ab23d2510954336db63451acc/actionpack/lib/action_dispatch/request/session.rb#L59))

```ruby
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の実体を追っていく

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



　flashは、`ActionDispatch::Flash::FlashHash`であることがわかった。この中身を追っていく([github](https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/actionpack/lib/action_dispatch/middleware/flash.rb#L111))



```ruby
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 削除

```ruby
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_flash`は`ActionDispatch::Routing::Redirection`の中で、読み込まれており、リダイレクトのたびに実行される。

# 4つのポイントに分けて確認する
　![スクリーンショット 2021-07-18 21.10.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611089/4579549c-3179-f549-1e1f-e1710e880981.png)


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

- コントローラ内のupdateアクションで、`flash`を作成
- ここでは、`session`には保存されていない

````ruby
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`メソッドが実行され、`session`に`flash`を保存

```ruby
def index
    binding.pry
    @tasks = Task.all
  	...
 end  
pry(#<#<Class:0x00007f95019d9228>>)> session[:flash]
=> {"discard"=>[], "flashes"=>{"success"=>"タスク「変更」を更新しました"}}
```



## 3. Viewでflashを表示

- `session`に保存していた`flash_hash`の情報を取得し、画面に表示

```erb
<% 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の中身もすべて削除されている

```ruby
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
  - アクションの結果を現在のページで表示したい時（永続的）に使う


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