背景
ASP.NET では、Windows アプリケーションのようなプログラミングが可能にしてあるために、Windows アプリケーションには無いが、Web アプリケーションでは問題になることがあります。その一つが「リロード」や「戻る」ボタンなど、ブラウザが用意している機能です。これらの機能は、作成しようとしているアプリケーションではなく、ブラウザというすでに完成しているアプリケーションが持つ機能であるため、抑止することが出来ません。その為、エンドユーザに「これこれの操作はしないでください」と説明して運用上回避するか(操作ミスで発生する場合がある)、それらの操作をされるものとして、された後に何らかの動作ではじくようにする必要があります。
ここでは、ブラウザの「リロード」によって多重登録されることを防ぐ方法を紹介します。
前提条件
- セッション変数が使えること
- ビューステートが使えること
- CustomValidator を配置する
コード
CustomValidator を追加しておく
private void Page_Load(object sender, System.EventArgs e) {
if (this.IsPostBack) {
} else{
string stamp = DateTime.Now.Ticks.ToString();
this.Session[WebHelper.TickStamp] = stamp;
this.ViewState[WebHelper.TickStamp] = stamp;
}
}
private void CustomValidator1_ServerValidate(object source
, System.Web.UI.WebControls.ServerValidateEventArgs args) {
this.Application.Lock();
string befreStamp = this.Session[WebHelper.TickStamp] as string;
string stamp = DateTime.Now.Ticks.ToString();
this.Session[WebHelper.TickStamp] = stamp;
this.Application.UnLock();
string vStamp = this.ViewState[WebHelper.TickStamp] as string;
if (befreStamp == null || vStamp == null || befreStamp.CompareTo(vStamp) != 0) {
args.IsValid = false;
} else {
args.IsValid = true;
this.ViewState[WebHelper.TickStamp] = stamp;
}
}
説明
まず、最初の要求があったとき、セッションとビューステートに、現在時間を元にしたスタンプを書き込みます。ポストバックされたとき、Load イベント終了後に、検証処理が行われます。検証処理の中で、セッションとビューステートのスタンプを比べ、違っていれば「再表示」「戻るの後のポスト」「連続ポスト」であると判断します。
ここで連続ポストはほぼ並行に処理されます。もしサーバがマルチ CPU であれば、本当に平行に処理されるかもしれません。そこで Application.Lock メソッド を使い、マルチスレッドを防止しています。シングル実行が保証される範囲内で、スタンプの取り出し、新しいスタンプの設定を行い、各要求で違うスタンプが参照されるようにします。
ここには書いていませんが、実行されたくないイベントハンドラの入り口で、Page.IsValid プロパティ を検査し、検証が成功しているときのみ、イベントを実行するようにします。
応用
ここではアプリケーション全体をロックしていますが、セッションオブジェクトのみロックする方法があります。その方がパフォーマンスは低下しないでしょう。
コーディングによっては、ユーザが「戻る」操作をしてしまったために画面の切り替えが全くできなくなるような場合があります。そのようなことがないように、脱出方法を用意しておきましょう。特に、連続ポストは注意が必要です。ブラウザが受け取るのは、最後の要求に対する返事です。つまり、「連続ポスト」と判断された要求に対する返事です。このため、ユーザは正しい要求に対する返事を受け取れなくなります。
参考
@IT で、きくちゃんさんがサーバーコントロール化されていますので、リンクを張ります。
ブラウザの「戻る」「更新」を検知するサーバーコントロール
投稿日時 : 2005年11月18日 21:34