PHPプログラミング

ワークショップ

ひと言掲示板を作る(23)

リロードによる多重投稿を防止する

「ひと言掲示板」に、再読み込みしたら同じ投稿が重複送信されてしまうのを予防する機能を実装します。

この記事のポイント

  • 自動リダイレクトでフォームの再送信を防ぐ
  • 成功メッセージをセッションに保存してリダイレクト後に表示する

目次

リロードするとPOSTデータが再送信される

前回まで、掲示板の管理画面を作成してきました。
今回は一般向けの掲示板に戻って、リロード(再読み込み)による投稿の多重送信を防ぐ機能を実装していきます。

Note

今回の機能は、デモ版の掲示板に寄せられた「リロードすると同じ投稿がされてしまう」というコメントを頂いて、追加をさせて頂きました。
ありがとうございます。
また他にも気になる点がございましたら、いつでもコメントをお寄せください。

多重投稿の防止機能がない場合は、次のようにブラウザを再読み込みすると同じ内容で繰り返し投稿されてしまいます。
まずは通常の1回目の投稿です。

1回目の投稿

ここで、ブラウザを再読み込みします。
このとき、フォーム再送信のメッセージが表示されます。
以下のサンプルはChromeの場合ですが、ブラウザによってメッセージは異なります。

ブラウザで再読み込み

再読み込みすると、全く同じ内容で投稿が実行されていることが確認できます。

同じ内容が繰り返し投稿される

この再読み込みによって繰り返し投稿されてしまう事象を解決していきます。

「ひと言掲示板を作る」の概要については「ひと言掲示板を作る」をご覧ください。
デモはこちら

前回までに作成したコードはこちら:Github

再読み込みで同じ投稿がされる原因

ブラウザで再読み込みすると、どうして同じ内容の投稿が繰り返されてしまうのか。
その原因から解説します。

form要素の送信ボタンなどを押してページを移動する場合、フォームに入力された値もパラメータとしてセットで送信されます。
これは通信方式がGETPOSTのいずれであっても同じです。
ここで、パラメータで受け取ったデータに問題がなければ、正常に掲示板への書き込みが行われます。

そこで、移動先のページで再読み込みを行うとします。
すると、先ほどのパラメータもセットで再読み込みが行われます。
システムとしては、また新しい投稿があったと判断して正常に処理を行ってしまうため、掲示板にも同じ内容で書き込みが投稿される、という流れになります。
つまり、システムから見たら1回目も2回目も、10回目も全て同じ「投稿されたデータ」となります。

そこで、このような再読み込みによるデータの多重送信を防ぐための方法として、大きく2つの方法があります。

  • トークンやセッションを使ってページ遷移の正当性確認する
  • 自動リダイレクトを行い、再読み込みをパラメータのない状態にする

トークンやセッションを使った確認方法はLaravelなどのフレームワークでも取り入れられている方法です。
しかし、今回のようにフレームワークを使っていない場合での実装は複雑になってしまうため、2つ目の自動リダイレクトを使った方法で実装していきます。

自動リダイレクトを実装

投稿が正常に完了したときのみ、自動リダイレクトが行われるように設定していきます。
このリダイレクトにはheader関数を使います。

コード例

<?php

-- 省略 --

session_start();

if( !empty($_POST['btn_submit']) ) {
	
	-- 省略 --

	if( empty($error_message) ) {
		
		// データベースに接続
		$mysqli = new mysqli( DB_HOST, DB_USER, DB_PASS, DB_NAME);
		
		// 接続エラーの確認
		if( $mysqli->connect_errno ) {
			$error_message[] = '書き込みに失敗しました。 エラー番号 '.$mysqli->connect_errno.' : '.$mysqli->connect_error;
		} else {

			-- 省略 --
		
			if( $res ) {
				$_SESSION['success_message'] = 'メッセージを書き込みました。';
			} else {
				$error_message[] = '書き込みに失敗しました。';
			}

			// データベースの接続を閉じる
			$mysqli->close();
		}

		header('Location: ./');
	}
}

-- 省略 --

投稿が成功した場合のメッセージも、このタイミングでセッションに保存するように変更を行います。
今まではリダイレクトせず、そのまま表示していたためシンプルに変数などで保存して表示すれば大丈夫でした。
しかしリダイレクトすると変数の中身も当然リセットされてしまうため、セッションやGETパラメータ(URLにデータを含める)などで値を渡す必要があります。
今回は他の場所でもセッションを利用しているため、投稿成功のメッセージも同様にセッションを利用します。

続くheader関数ですが、「Location:」の後ろにリンクを指定します。
今回は自分自身を呼び出すため、「./」としています。

次に、先ほどセッションに保存した投稿成功メッセージを表示するための部分を修正しましょう。

コード例

-- 省略 --

<body>
<h1>ひと言掲示板</h1>
<?php if( empty($_POST['btn_submit']) && !empty($_SESSION['success_message']) ): ?>
	<p class="success_message"><?php echo $_SESSION['success_message']; ?></p>
	<?php unset($_SESSION['success_message']); ?>
<?php endif; ?>

-- 省略 --

if文の条件式では、2つのことをチェックしています。

  • POSTパラメータの「書き込む」ボタンが押されていないか
  • 表示する成功メッセージのセッションがあるか

いずれもempty関数を使ってチェックを行い、両方trueであれば、続く1行でセッションに入っているメッセージを出力します。

セッションにメッセージが残っていると、掲示板を開くたびに投稿成功メッセージが表示されてしまいます。
そこで、次の行のunset関数で1度表示したメッセージをセッションから削除するために実行します。

今回のコード改修で、今まで成功メッセージを格納していた変数「$success_message」は不要になるため、「// 変数の初期化」で初期化している箇所も削除してしまって大丈夫です。

以上で多重投稿の防止機能が実装できました。
試しに投稿して、再読み込みをしてみましょう。

まずは普通に書き込んでみます。

通常通りの書き込みを行う

書き込みが成功すると、上部に成功メッセージが表示され、投稿したメッセージも表示されます。

正常に書き込まれたときの表示例

続いて、ブラウザの再読み込みをしてみます。

再読み込みした後の表示例

先ほど上部に表示されていた成功メッセージは非表示になり、多重投稿がされていないことも確認できました。


今回まで作成してきた「ひと言掲示板」は、一通りの機能を実装してきました。
当ワークショップはここまでとなります。

ワークショップへのフィードバックやご要望はいつでも大歓迎です。
記事の下部にある「こちらの記事は役に立ちましたか?」のボタンを押すと表示されるメッセージ欄か、お問い合わせページよりお気軽にお送りください。
お待ちしております。

今回作成したコード:Github

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

ありがとうございます。
コメントを送信しました。