- PR -

ASP.NETの二重クリック防止について

投稿者投稿内容
suwa
会議室デビュー日: 2005/08/26
投稿数: 8
投稿日時: 2005-08-26 16:59
はじめまして。
ASP.NETで開発をしております。

ボタンを何度もクリックして、同じ処理が複数回実行されるのを防止したいのですが、
分からないことがあり、どなたかご教授いただければと思います。
過去ログにも同じような質問があり、
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=6531&forum=7
引用:
この場合、例えばSessionとViewStateの両方にカウンタを保持してポストバック毎にインクリメントし、双方の値が一致するかどうかをチェックする、という手はどうでしょうか。


上記のきくちゃん様のコメントを参考に、
WebForm1.aspxにボタンを1つ付けただけの画面に
下記のコードを記述して検証してみました。
WebForm2.aspxでは確認として、
Session["CountSession"]の値と、Session["CountOnClick"]を表示させます。

ところが連続してボタンをクリックした場合、
Session["CountSession"]の値は1になりますが、
Session["CountOnClick"]の値はボタンをクリックした回数が表示されます。
つまりButton1_Click()メソッドが複数回呼ばれています。
これを1回のみ呼び出されるようにするにはどうしたら良いのでしょうか。

ちなみに、Button1_Click()メソッドの最初に
Session["CountSession"]の値と["CountViewState"]との値が異なったらreturnする、
というコードを記述した場合、1回のみ呼び出されるようになるようですが、
今度はWebForm2.aspxにリダイレクトされなくなってしまいました。
何か根本的な勘違いをしているのでしょうか。

コード:
private void Page_Load(object sender, System.EventArgs e)
{
	if (Page.IsPostBack)
	{
		int countSession = (int)Session["CountSession"];
		int countViewState = (int)ViewState["CountViewState"];

		if (countSession != countViewState)
		{
			return;
		}

		countSession += 1;
		countViewState += 1;
		Session["CountSession"] = countSession;
		ViewState["CountViewState"] = countViewState;
	}
	else
	{
		Session["CountSession"] = 0;
		ViewState["CountViewState"] = 0;
		Session["CountOnClick"] = 0;
	}
}


private void Button1_Click(object sender, System.EventArgs e)
{
	int countOnClick = (int)Session["CountOnClick"];
	countOnClick += 1;
	Session["CountOnClick"] = countOnClick;

	// 時間のかかる処理をシミュレート
	Thread.Sleep(5000);

	Response.Redirect("WebForm2.aspx");
}

burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2005-08-26 17:16
引用:

Session["CountSession"]の値と["CountViewState"]との値が異なったらreturnする、
というコードを記述した場合、1回のみ呼び出されるようになるようですが、
今度はWebForm2.aspxにリダイレクトされなくなってしまいました。
何か根本的な勘違いをしているのでしょうか。



ボタンを連打したとき2回目以降のリクエストは
Session["CountSession"]の値と["CountViewState"]との値が異なったらreturnする
のif文でreturnされます。returnされるということはブラウザにレスポンスが返されるわけです。
すると当然WebForm2.aspxには遷移しません。

suwa
会議室デビュー日: 2005/08/26
投稿数: 8
投稿日時: 2005-08-26 20:46
引用:
ボタンを連打したとき2回目以降のリクエストは
Session["CountSession"]の値と["CountViewState"]との値が異なったらreturnする
のif文でreturnされます。returnされるということはブラウザにレスポンスが返されるわけです。
すると当然WebForm2.aspxには遷移しません。



burton999様ありがとうございます。
最初のリクエストが処理をしている間に、Button1_Click()メソッド内でreturnさせると
そちらがレスポンスとして返され、最初のリクエストのレスポンスはなくなるのですね。
すると2回目以降のリクエストのときはPage_Load()側で判断して、
Button1_Click()を呼ばないようにコードを記述すれば良いのでしょうが、
どのようなコードになるのか分かっておりません。。。
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2005-08-26 21:53
ボタンをクリックしたらサーバーの処理が完了するまでボタンを
使用不可にしたらどうでしょうか。

サンプルを作成しましたのでご覧ください。
http://www.friendlysw.com/aspnet/articles/app/040415-1.aspx

_________________
ASP.NET+Ajaxサンプル集 | JavaScript+Ajaxサンプル集
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2005-08-26 22:04
Access様のサンプルにあるとおり、クライアントスクリプトでボタンを無効にするのが一般的です。
しかし、これでも100%対応できるわけではないので、DBの更新系の処理などは
サーバーサイドでもチェックしたほうがいいと思います。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-08-26 22:12
 イベント内でチェックする必要があります。が、全イベントでそんなことやってられないので。。。

 CustomValidator を追加します。クライアントスクリプトは登録しません。サーバ側検証イベントを作成してください。
 サーバ側検証イベントに、チェックしている処理を移動します。
 セッションの値を取り込んで書き換えるまでを、並列に処理されないように工夫する必要があります。
 各イベントの先頭で、Page.IsValid を検証し、true であればイベント処理を実行します。


 IsValid の内容にかかわらず、イベントは実行されるとは・・・orz

一例:(未検証)
コード:
private void Page_Load(object sender, System.EventArgs e) {
	if (this.IsPostBack) {
	} else{
		// 初期処理
		string stamp = DateTime.Now.ToLongTimeString();	// これは適宜変更のこと
		this.Session["CHECK"] = stamp;
		this.ViewState["CHECK"] = stamp;
	}
}

private void CustomValidator1_ServerValidate(object source, ServerValidateEventArgs args) {
	this.Application.Lock();	// 並列処理させない
	// アプリケーションをロックする必要はないけど。。。
	string befreStamp = this.Session["CHECK"] as string;
	string stamp = DateTime.Now.ToLongTimeString();	// これも変更のこと
	this.Session["CHECK"] = stamp;
	this.Application.UnLock();
	string vStamp = this.ViewState["CHECK"] as string;
	if (befreStamp == null || vStamp == null || befreStamp.CompareTo(vStamp) != 0) {
		args.IsValid = false;
	} else {
		args.IsValid = true;
		this.ViewState["CHECK"] = stamp;
	}
}

private void Button1_Click(object sender, System.EventArgs e) {
	if (!this.IsValid) return;	// ←これではじく

	LongLongWaiting wait = new LongLongWaiting(long.Parse(this.TextBox1.Text));
	Stocker.Add(this.Session.SessionID, wait);
	wait.wait(this.Session.SessionID);
	this.Response.Redirect("./waitForm.aspx");
}





> // 時間のかかる処理をシミュレート
> Thread.Sleep(5000);
 これ、大丈夫ですか?途中でアボートしませんか?デフォルトでは、90秒ぐらいでアボートすると思います。
GDNJ のスレッドに誘導

_________________
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2005-08-27 06:14
引用:

しかし、これでも100%対応できるわけではないので、DBの更新系の処理などは
サーバーサイドでもチェックしたほうがいいと思います。


英語ですが参考までに。
http://aspalliance.com/687
http://www.tectonicconcepts.com/netproducts.aspx?id=84667942
_________________
ASP.NET+Ajaxサンプル集 | JavaScript+Ajaxサンプル集
suwa
会議室デビュー日: 2005/08/26
投稿数: 8
投稿日時: 2005-08-27 11:44
引用:
ボタンをクリックしたらサーバーの処理が完了するまでボタンを
使用不可にしたらどうでしょうか。



ボタンをクリックしたらAccess様のサンプルとほぼ同様のスクリプトでボタンを非活性にさせております。
最初のコードの「Thread.Sleep(5000);」の部分は実際にはDB更新処理となりますので、
出来る限り、サーバー処理が複数回呼ばれるのを防止したいと考えて、今回の投稿に至りました。

週末のため、ちょうど今検証ができませんが、
(1)Jitta様のCustomValidatorコントロールを使う方法
(2)Jitta様ご紹介のGDNJ のスレッド
(3)Access様ご紹介のサイト
を参考に週明けにまた検証したいと思います。

引用:
> // 時間のかかる処理をシミュレート
> Thread.Sleep(5000);
 これ、大丈夫ですか?途中でアボートしませんか?デフォルトでは、90秒ぐらいでアボートすると思います。


こちらの環境ではアボートは発生しておりません。
実際にはここはDBの更新処理が入りますが、まだ未実装のため、代わりにSleep()を入れております。
検証目的であってもこれは宜しくなかったでしょうか。

スキルアップ/キャリアアップ(JOB@IT)