前回
は問題提起だけしたのですが、提起した問題がイメージしにくかったように思うので論点を整理した上で
何がいったいそんなに難しいのかを解説してみたいと思います。

簡素な図ですが、ボックスは画面を、矢印は画面の遷移(というかHTTPリクエスト)をあらわしています。
A → B → Cという遷移をする画面構成なのですが、画面Aから画面Bを開く際の矢印(2)の部分で
ブラウザ上で「別ページで開く」としていたとします。
そして、矢印(3)と進んだところで、別ページに開かれていた画面Aから再度画面Bを開くとします。
その後、画面BからCへと遷移します。最終的にブラウザの二つのウィンドウ(もしくはタブ)が開き
画面Cが二つ表示されている状態になります。
上記のような操作をしたとして、図の水色の部分、つまり、(1)(2)(4)のHTTPリクエストの処理の際には
セッションからある値が参照できて、(3)(5)のHTTPリクエスト処理の際には該当の値は存在しないように
振舞うセッション管理機構を作れるのか?というのが前回の問題提起だったのです。
画面の遷移を追う
HTTPではリクエストするURLとHTMLのFORMタグ内の入力データが送られてくるわけですが、
どこの画面から遷移してきたかということを確実に知る方法がありません。
一応、リファラ(referer)という遷移もとの画面を通知するために定義されているHTTPヘッダフィールドは
存在するわけですが、ブラウザの設定で送信しないようにしたり、また、ユーザ定義の固定値を送ったりできるわけです。
また、リファラが信頼できることを前提としたとしても、リファラが送るのはURLだけなので、
図で言うところの(3)と(5)の矢印の区別がつかないのです。画面BのURLからリクエストが来たということだけでは、
複数開いたウィンドウのどこの話なのか把握できないのです。
そこで、画面を表示する際に一意な値をinputタグのhiddenなどで持たせておいて、
画面遷移の際にその値を送るようにするとします。
図ではSEQという値がこれに相当します。ボックス内のSEQは画面に振られたIDで、
矢印に付記されたSEQは遷移元の画面から送信されるSEQのIDです。
こうすることで(3)と(5)の矢印を区別することができ、(5)の矢印がSEQ4の画面Bからの遷移であり、
SEQ2の画面Bから別ウィンドウで開いたわけではないことが追跡できるようになります。
同一ウィンドウでの遷移か、別ウィンドウで開いたのか
サーバ側では同一ウィンドウでの遷移か、別ウィンドウで開いたのかを判別することができません。
A → B → Cと順に別ウィンドウで開いていったとしても、HTTP的には同じリクエストが送られるので
サーバ側で判別する術がないのです。どこのウィンドウに表示するというのはブラウザが受け取った
リクエストをどのように処理するのかという話ですから、サーバ側でとやかく言うことはできないのです。
しかし、別のウィンドウが開かれたらしいということは検出することができます。
それが矢印(4)の部分なのですが、(2)の時点でSEQ:1からの遷移を意味するHTTPリクエストがされていますから、
再びSEQ:1からの遷移を意味するリクエストが来た時点で別ウィンドウで開かれた、
もしくはブラウザのヒストリーバックなどのキャッシュで戻ってそこからリクエストを再送した
といったものであることが想像できます。(リロードとか2重送信の可能性もありますが)
しかし、別ウィンドウ操作などは同一SEQが再送されるまで検出できないため、
タイミング的に処理しにくいものがあります。
図では(3)の時点で画面Bから画面Cへと遷移しています。じゃぁもう画面Aは捨てられたのだろう、
と思いきや、実は別ウィンドウで操作していたんだよ、ということが(4)のリクエストの時点で初めてわかるのです。
セッションに変数スコープを持たせようとした場合、(3)のリクエストがあった時点で水色部分の
スコープを脱したので該当スコープで宣言されたセッションの値を破棄する、としてしまうと
(4)の遷移をすることができなくなってしまうわけです。
以上のことから、スコープ内での分岐に制限を加えるなどの方法論をとるか、
セッション情報を履歴管理して(4)のリクエストの時点でコピーされたメモリ空間を提供するなどの方法論になります。
いずれにせよ、簡単に制御できるフレームワークとできないことは容易に想像がつくと思います。
投稿日時 : 2007年10月9日 20:02