入力画面でSubmitボタンを連打した時の挙動[IT]
結構昔に僕が作ったシステムでBtoCなサイトがある。
よくあるフォーム画面で以下のような形。
POST 302転送
入力画面(A)⇒ DB登録(B)⇒ 完了画面(C)
なんか最近全然その単語を聞かなくなったPRGパターンとかいうやつ。
Post ⇒ Redirect ⇒ Get
RFC的にはリダイレクトされたリクエストでクライアントがメソッドを変更する事を許さないと規定しているはずなのに、リダイレクト時にPOSTしてくるブラウザは見たことない…と、ここではその話は置いとこう。
実際に表示される画面は上でいうAとCなんだけど、この両方の画面にJavascriptのアクセス解析ツールを仕込んでいた。まぁGoogleAnalytics(以下GA)なんだけどね。で、この画面で些細なんだけど以下の問題が発生した。
- データベースに入力した内容は保存されているものの、GAだとCにアクセスされた形跡はない
- 但し、Aはアクセスされている
- 毎回ではなく数あるうちの数回だけ発生している
で、このページは以下のような仕様となっている。
- Aの画面でワンタイムトークンを発行している
- Bの画面でDB登録前にワンタイムトークンをチェックしている
- チェックが失敗した場合、登録処理、リダイレクトはせずに403をレスポンスとして返す
- フォームのSubmitボタンは連打可能
上3つはよくあるCSRF対策。詳細は割愛。
で、最初はたまたまGAのJavascriptでエラーが起こったんじゃないかとかそういうこと考えたりPOSTした瞬間にブラウザ落としたりしたのが原因じゃないかと思ってた。そっち可能性も0じゃないんだけど、よくよく考えると以下の感じの挙動が問題を起こしたんじゃないかなという結論に達した。
- ブラウザが登録リクエスト送信(1回目)
- サーバ側で登録処理が行われ、302レスポンスが行われる
- 302転送を行わずブラウザが登録リクエスト送信(2回目)
- サーバ側で403レスポンスが行われる。
で、実はここからが本題。まぁ結果としては上の挙動をブラウザが取ったことにより問題となったんだけど、Firefoxだけ挙動が異なった。まぁ調べたのはIE,Chorme,Firefoxの3つなんだけどね。
- ブラウザが登録リクエスト送信(1回目)
- サーバ側で登録処理が行われ、302レスポンスが行われる。
- 画面CのGET処理が行われる。
- ※
- 302転送を行わずブラウザが登録リクエスト送信(2回目)
- サーバ側で403レスポンスが行われる。
こういった挙動。で、※の部分なんだけどさすがに画面Cの画像とかはリクエスト送ってないよね…。可能性は0じゃないけど調べてない。とりあえず30xの場合は転送後に次のリクエストが送られるような挙動を示した。
まとめ
だからなんだって話なんだけど今まで連続リクエスト時の挙動なんて全く意識してなかったからまとめてみた。普通にJavascriptで2重POSTされないようにすれば解決する話。
とりあえずSubmitボタンを連打した際はブラウザによってちょっと挙動が違うよという結論です。
配列の結合 [PHP]
主に array_merge() と +演算子の違いについて。
ぐぐればいっぱい出てくるんだけどよく忘れるので備忘録。
<?php // PHP 5.4.17 $array1 = array( 'hoge' => 'udon', 0, 1, ); $array2 = array( 1 => 'a', 'b', 'hoge' => 'soba', 'fuga' => 'ra-men', ); var_export(array_merge($array1, $array2)); var_export($array1 + $array2);
■array_merge()の場合
array ( 'hoge' => 'soba', 0 => 0, 1 => 1, 2 => 'a', 3 => 'b', 'fuga' => 'ra-men', )
- インデックスが重複した場合は後勝ち
- 但し、インデックスが数値の場合は振り直しを行い追加する
■+演算子の場合
array ( 'hoge' => 'udon', 0 => 0, 1 => 1, 2 => 'b', 'fuga' => 'ra-men', )
- インデックスが重複した場合は先勝ち
- 前の配列に重複インデックスがない場合のみ、後の配列の要素を追加する
どうも分かりづらいなぁと思うのは僕だけかな?