HTTPがステートレス志向であることに起因していますが、サーバサイドでの画面描画を想定したSpringなどのバックエンドフレームワークで画面描画を行う際は、サーバサイドで画面でデータをどのようなスコープで引き継いでいるのかそのデータライフサイクル引継を意識しながら設計・開発する必要があります。フラッシュスコープなるものの存在を知ったので、その使い分けを簡単にまとめてみました。
一般的にどのようなスコープが存在するのか?
スコープ名 | データのライフサイクル | データの参照範囲 | 想定されるユースケース | 備考 |
---|---|---|---|---|
リクエストスコープ | リクエストの開始から終了まで存続。 | 同一リクエスト内。 | ユーザのアクションに応じた一時的なデータのやり取り。 | |
フラッシュスコープ | 現在のリクエストから次のリクエストまで存続。リダイレクト後の最初のリクエストで消去。 | 現在のリクエストから次のリクエストまで。 | リダイレクト先での通知メッセージの表示など。 | SpringではRedirectAttributes経由で取得 |
ビュースコープ | 特定のビューが表示されている間存続。新たなビューに遷移すると終了。 | 同一ビュー内。 | 同一ページ内のユーザインタラクションに応じたデータ保持。 | ビュースコープは、JavaServer Faces(JSF)などの一部のWebフレームワークに存在 |
セッションスコープ | ユーザーセッション全体に渡り存続。ユーザーがWebサイトを閉じたり、一定時間活動がないと終了(またはタイムアウト)。 | 同一ユーザーセッション内。 | ログイン情報の保持、ショッピングカートの状態管理など。 | Javaフレームワークでは一般的にHttpSession経由で取得。 |
アプリケーションスコープ | Webアプリケーションのライフサイクル全体で存続。 | 全ユーザ、全リクエスト。 | サイト全体の設定や共有データの管理。 |
セッションスコープとフラッシュスコープの使い分け方針
フラッシュスコープの利用:
- あるリクエストから別のリクエストへ一時的なデータを引き継ぐ。例えば、リダイレクト後のページでユーザーに表示する情報(成功メッセージ、エラーメッセージ、フォームの入力値等)を引き継ぎたい場合。
- 状態の少ない(stateless)アプリケーション設計に貢献します。セッションデータの使用を減らすことで、アプリケーションのスケーラビリティが向上します。
セッションスコープの利用:
セッションスコープはユーザーセッション全体で有効で、主に以下のようなケースで使用します。
- ユーザーがシステムにログインしている間、ユーザー情報を保存したい場合。
- ショッピングカートのような、ユーザーセッション全体にわたって状態を維持する必要がある機能を提供したい場合。
どちらのスコープを使用するかは、あくまでユースケースによります。
少し悩んだ点(本記事を投稿するきっかけ)
- セッションスコープは従前の通り、リダイレクトを前提としたものですが、リダイレクトは伴わないものの次の画面遷移まで持ち続けたいというケースでは内部的にFlashMapを利用することも可能。(ただし低レベルAPIを利用することになるため、将来のバージョンアップなどに追随できなくなる可能性もあり、セッションスコープを使うことに)
サンプル.java
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.RequestContextUtils;
public void someMethod() {
FlashMap flashMap = new FlashMap();
flashMap.put("businessList", businessList);
ServletWebRequest servletWebRequest = new ServletWebRequest(request);
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
if (flashMapManager != null) {
flashMapManager.saveOutputFlashMap(flashMap, servletWebRequest);
}
}