HTTP はステートレスなプロトコルなので、Web アプリケーションを構築するには何らかの状態維持のメカニズムが必要であり、ASP.NET はセッション状態、ビュー状態、コントロール状態、クエリ文字列、クッキー、隠しフィールドを使って各リクエスト間の状態を維持する。
ユーザーは気紛れであり、各ページ間を好き勝手に行き来する。時には、あろう事かローカルに保存されている過去のページを参照し、過去の状態でリクエストする。要はブラウザの「戻る」ボタンを多用する。
そういったユーザーの動作を制限するのは良い作法ではない。なので、ユーザーが正常な状態を持ってリクエストする事を前提にしてWeb アプリケーションを構築してはならない。
ところで、ビュー状態は何故必要なのだろうか。多くの Web アプリケーションフレームワークにはビュー状態という概念はない。ビュー状態とは、一般的には隠しフィールドに格納された「レスポンス時の表示の状態」の事である。主な役目は、ASP.NET イベントの起動のためであり、ビュー状態のおかげで、イベントドリブンなプログラミングスタイルが可能になる。ビュー状態は想像以上に多くの状態を持っていて、何も考えないでいるとかなりのサイズになっていたりする。
各ページ間のビジネスロジックに用いる状態は主にセッション状態を使う。ユーザーが一連の操作で確定した最新の状態がセッション状態に保持されている。
話を戻す。
ユーザーは過去の状態をいきなり POST する。ブラウザの戻るでヒストリを出してきて、過去のビュー状態とポストデータを送りつける。セッション状態は常に最新の最終結果を保持しているので、ビュー状態、ポストデータとセッション状態を付き合わせたとき、妙な不一致が生じて最悪アプリケーションは停止する。
今、表示すべきデータA、データB、データCがある。この状態をクライアントに返すと、各データの表示されている情報がビュー状態の一部となる。表示には GridView 等、行が増えたり減ったりするものにしよう。
ユーザーが操作を繰り返し、セッション状態はデータA'、データB、データDとなった。データAは更新され、データCは削除され、データDが追加されたわけだ。
ここでユーザーは、タイムマシンを夢見たのかブラウザで戻ってヒストリを出してきた。ユーザーはデータAをデータA''に、そしてデータCを削除した事をすっかり忘れて(忘れた事にして)、データCをデータC'にして更新した。サーバー側ではどのような処理を行うべきなのだろうか。
ランタイムは ViewState と PostData の食い違いを発見し、イベントを駆動する。ここではデータA、データCに関わる情報が変わっているので、テキストボックスにバインドされているとすれば、TextChanged イベントを駆動する。
アプリケーションコードでは、1 番目と 3 番目のテキストボックスの TextChanged イベントが発生したと認識し、Session 状態のデータA' をデータA''に、データDをデータC'に更新する。
もちろんこんな間抜けな実装はしてはいけないが、ユーザーはそうしたいと考えている。ユーザーの意図を優先すべきなのだろうか?
つづく。