休みの日に、簡単なチャットアプリを作ったのだけれど、リロードによる二重投稿に嵌ったよ。制作中のアプリにも搭載する予定の機能なので、備忘必須。
二重投稿の防止
laravelのグローバルなsessionヘルパを使います。
・ blade.phpから、乱数をpostでcontrollerに送信し、sessionにも登録しておく。
・ controller側では、受信したpostの値と、sessionの値を比較して、両者が異なっていたら処理を中断する。
sam1
<?php
//if(!(session_start()) { session_start(); }
try{ session_start();}catch(Exception $ex){}
$token = rand();
session(['token'=>$token]);
?>
<form action="/sam2" method="post">
{{csrf_field()}}
<input type="text" name="name">
// 目印を POSTで送信する
<input type="hidden" name="token" value="<? echo $token ?>">
</form>
controller
public function sam2(Request $request)
{
//if(!session_start()){ session_start(); }
try{ session_start(); }catch(Exception $e){}
// 同値判定
// セッションの目印と、POST送信された目印が同じかどうか判定
try
{
if($_POST['token'])
{
if($_POST['token']==session('token'))
{
session(['token'=>'']);
// formされた情報に、必要な処理をする
}
}
}catch(Exception $e){}
}
controllerのtry-catch
#unset処理に関して
上記ではsession(['token'=>'')として初期化しているが、グローバルヘルパではなく、グローバルセッションを使う場合、$_SESSION['pass'] と $_POST['pass'] の同値判定をした後に、リロードや連続投稿を防止するために、$_SESSION(若しくは$_POST)をunsetするが、高速リロード、高速連続投稿の時には、unset後、すぐに同じ同値判定を行おうとして、unsetした$_SESSION(若しくは$_POST)を評価し、undefineエラーが投げつけられることがあった。これを回避するためにtryで処理したら大丈夫だった!
同値判定を関数にして風通しよくする
controller
public function ReceiveForm(){
$result = $this->sessionCheck($request);
if( $result == "" ){ return false; }
// 処理
}
function sessionCheck($request){
try{ session_start() }catch(Exception $e){}
try{ if( session('token') ){
if( session('token') === $_POST['token'] ){
$form = $request->all();
session(['token'=>'');
unset($form['_token']); // これはcsrf_field()のtokenを削除してる
unset($form['token]); // これはPOST送信されてきたtokenを削除してる
return $form;
}
}catch(Exception $e){}
$form=[];
return $form;
}