Web アプリケーションでは、複数の要求にまたがるセッションの状態を維持する必要があることがしばしばあります。一般に、J2EE[tm] アプリケーション内のセッション状態は、これらの層のどれにも保存できます。
各層には、セッション状態の保存に関して長所も短所もあります。アプリケーションは複数の層にセッション状態を保存でき、異なる層にセッション状態を分割して保存する方法を選択することもできます。HttpSession による Web 層への状態の保存はよく知られており、ステートフルセッション Bean を利用するなどの他の選択肢は多くの場所で取り上げられているため、この対処法では、クライアント層へのセッション状態の保存に焦点を当てます。この対処法は、HttpSession を使用してセッション状態を保持するのではなく、あるいはその方法に加えて、クライアント側にセッション状態を保存する必要がある場合に有用です。この対処法の主な要件の 1 つは、状態を安全に保存することです。そして、この対処法では、それが達成されます。
Web アプリケーションがクライアント層にセッション状態を保存する方法はいくつかあります。考えられる対処法の 1 つは、Cookie を使用してクライアントに状態を保存する方法です。ただし、Cookie にはサイズ制限があり、クライアントにセッション状態を保存する場合は最適ではありません。
HTML の隠しフォームフィールドと、シリアライズとデシリアライズ用のコード、エンコーダ、その他のいくつかのトリックを使用して、より安定した仕組みをデザインすることができます。ビューが、そこを離れたのと完全に同じように再生成できるようにするには、URL パラメータと Java オブジェクトの両方をエンコーディングする必要があります。この対処法は、Web アプリケーションがクライアントに状態を保存するための再利用可能な手段を提供することができます。対処法と例を簡単に見てみましょう。
HTML は、非表示フィールドとしてフォームに状態を置く手段を提供します。この状態は、後で POST で読み出すことができます。コード例 1 は、非表示フィールドを使用して HTML フォームに状態を保存する方法を示しています。
<form action="controller.do" method="POST">
<input type="HIDDEN" name="var1" value="value 1">
<input type="SUBMIT" value="Push Me">
</form>
コード例 1: 非表示フィールドを用いたセッション状態の保存
上記のコード例の場合、「Push Me」ボタンをクリックすると、フォームおよびデータが、HTTP POST として、controller.do
にマッピングされている Web コンポーネントに送信されます。この対処法では、HTTP POST メソッドではなく GET メソッドではうまくいかないことに注意してください。これは、GET がすべてのフォームフィールドを URL パラメータとしてエンコーディングし、非常に長い URL が作成されて、大部分の一般的なブラウザおよび Web コンテナがサポートしている長さを超えるかもしれないためです。また、HTTP GET メソッドは、idempotent とみなされる要求に使用すべきでることも覚えておいてください。そうすることによって要求をローカルに、また中間物によってキャッシュすることができます。<a href="someurl">
タグのようなリンクを作成できますが、クライアント側状態を含むフォームを送信する際は、それらタグに関連付けられた JavaScript[tm] イベントを使用します。コード例 1 のフォームを送信するリンクは、次のようになります。
<a href="#" onclick="document.forms[0].submit(); return false;">
Java[tm] 言語には、オブジェクトをシリアライズして、それらが以前の 1 時点にあったときと同じ状態で再作成できるようにする仕組みが用意されています。エンコーディング方式との組み合わせてシリアライズされた内容は、クライアントに送信される HTML ページに書き込まれます。上記の例の場合、エンコーディングされ、シリアライズされたデータは、var1
属性の値として書き込まれます。ページの保存するデータ量が多い場合は、シリアライズされた内容を圧縮することもできます。
フォームの POST 送信先の Web コンポーネント (上記の例では controller.do
にマッピングされているサーブレット) は、HttpServletRequest
API を使用して非表示フォーム変数にアクセスできます。この Web コンポーネントは状態をデシリアライズし、その状態に基づいてアクションを実行します。
クライアント側状態を使用して、HttpSession に保存されているデータ量を減らすことができます。これは、いくつかの Web ページに状態を分散するのに役立ち、分散した状態はすべてクライアント側に保持されます。これは、サーバー側で必要なメモリーが減らすのに役立つことがあり、スケーラビリティが向上します。ただし、必ずしも状態全体をクライアントに移すことが適切というわけではありません。クライアント側状態の使用では、次のことに注意してください。
RequestDispatcher.forward()
API を使用するサーバー側リダイレクトは、元の HttpServletRequest
および HttpServletResponse
オブジェクトにアクセスして、同じコンテナ内の他の Web コンポーネントに状態を転送できるようにします。これらのコンポーネントが異なるコンテキストにある場合は、それらコンポーネントは、デシリアライズされたクラスを目的のオブジェクトに型キャストする機能を持っているか、リフレクション API を使用して、状態にアクセスする必要があります。 クライアント側に保存するのが最適な状態としては、オブジェクトなどの、ページ固有の状態があります (JavaServer[tm] Faces は、ビュー状態を保存する際のオプションとして、非表示フォーム要素にクライアント状態を置く方法を使用します)。そうしたオブジェクトとしては、通常は HttpSession に保持されるユーザー別のオブジェクトや、ショッピングカート、検索結果、ウィッシュリスト、ユーザー情報などのオブジェクトが適しているかもしれません。Web アプリケーションの構成状態などの状態や、画像などを含む大きなオブジェクト、ユーザー間で共有されるオブジェクトは、クライアント側で保持すべきではありません。
クライアントに状態を保存することが最適な対処法でないことがあります。クライアントでの状態の保存には、パフォーマンスの問題や帯域幅の使用量の問題があります。このドキュメントで説明する方法での状態の保存は、ケースバイケースで考えてください。
クライアントに保存されている状態は、次のような、さまざまな種類の攻撃を受けやすくなります。
セッション状態を保存するためのコードは、JSP ページと Web 層クラスに直接埋め込むことができますが、再利用するには、そのコードをカスタムタグに入れる方がより適切です。以降の要求で再構成できる方法で必要な隠しフィールドを含む HTML フォームを生成するとともに、URL パラメータおよび Java オブジェクトをエンコーディングするのは、いくぶん退屈な作業かもしれません。しかし、再利用可能なカスタムタグライブラリは、URL パラメータと会話状態のシリアライズとエンコーディングを行うのに必要な複雑な細部を隠すことができます。
この方法の実装の詳細は、クライアントでのセッション状態の保存: デザインの詳細を参照してください。