ワークショップ
ひと言掲示板を作る(23)
リロードによる多重投稿を防止する
「ひと言掲示板」に、再読み込みしたら同じ投稿が重複送信されてしまうのを予防する機能を実装します。
この記事のポイント
- 自動リダイレクトでフォームの再送信を防ぐ
- 成功メッセージをセッションに保存してリダイレクト後に表示する
目次
リロードするとPOSTデータが再送信される
前回まで、掲示板の管理画面を作成してきました。
今回は一般向けの掲示板に戻って、リロード(再読み込み)による投稿の多重送信を防ぐ機能を実装していきます。
Note
今回の機能は、デモ版の掲示板に寄せられた「リロードすると同じ投稿がされてしまう」というコメントを頂いて、追加をさせて頂きました。
ありがとうございます。
また他にも気になる点がございましたら、いつでもコメントをお寄せください。
多重投稿の防止機能がない場合は、次のようにブラウザを再読み込みすると同じ内容で繰り返し投稿されてしまいます。
まずは通常の1回目の投稿です。

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

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

この再読み込みによって繰り返し投稿されてしまう事象を解決していきます。
「ひと言掲示板を作る」の概要については「ひと言掲示板を作る」をご覧ください。
デモはこちら
前回までに作成したコードはこちら:Github
再読み込みで同じ投稿がされる原因
ブラウザで再読み込みすると、どうして同じ内容の投稿が繰り返されてしまうのか。
その原因から解説します。
form要素の送信ボタンなどを押してページを移動する場合、フォームに入力された値もパラメータとしてセットで送信されます。
これは通信方式がGET、POSTのいずれであっても同じです。
ここで、パラメータで受け取ったデータに問題がなければ、正常に掲示板への書き込みが行われます。
そこで、移動先のページで再読み込みを行うとします。
すると、先ほどのパラメータもセットで再読み込みが行われます。
システムとしては、また新しい投稿があったと判断して正常に処理を行ってしまうため、掲示板にも同じ内容で書き込みが投稿される、という流れになります。
つまり、システムから見たら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
こちらの記事は役に立ちましたか?
コメントありがとうございます!
運営の参考にさせていただきます。
記事一覧
- ひと言掲示板を作る(1) ひと言掲示板を作る
- ひと言掲示板を作る(2) メッセージの入力フォームを作る
- ひと言掲示板を作る(3) メッセージのデータを受け取る
- ひと言掲示板を作る(4) ファイルにデータを保存する
- ひと言掲示板を作る(5) ファイルからデータを読み込む
- ひと言掲示板を作る(6) 投稿完了メッセージを表示する
- ひと言掲示板を作る(7) 投稿の未入力バリデーションをつける
- ひと言掲示板を作る(8) 投稿されたデータをサニタイズする
- ひと言掲示板を作る(9) 投稿データの保存にデータベースを使う
- ひと言掲示板を作る(10) 掲示板のデータベースとテーブルを作成する
- ひと言掲示板を作る(11) 投稿データをデータベースに登録する
- ひと言掲示板を作る(12) データベースからデータを取得する
- ひと言掲示板を作る(13) コードを整理する
- ひと言掲示板を作る(14) セッションで表示名の入力を省略する
- ひと言掲示板を作る(15) 管理ページを作成する
- ひと言掲示板を作る(16) ログインページを作る
- ひと言掲示板を作る(17) 投稿データをCSV形式でダウンロードする
- ひと言掲示板を作る(18) 投稿データをダウンロードする件数を指定する
- ひと言掲示板を作る(19) 投稿メッセージの編集ページを作る
- ひと言掲示板を作る(20) メッセージを編集する機能を実装する
- ひと言掲示板を作る(21) 投稿メッセージの削除ページを作る
- ひと言掲示板を作る(22) 管理ページにログアウトを実装する
- ひと言掲示板を作る(23) リロードによる多重投稿を防止する
ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。