二重送信問題について調べたことをまとめた
戻るボタンと右クリックをアプリ側で禁止にしている現場で働いている。
ユーザーが同じ更新内容を意図せずに重複して発行しないためで、こういう問題は二重送信問題と言われるらしい。
(Double Submit Problemって検索したらたくさん出てくるよ)
さすがに2017年にもなってそれはダサいと思うので、今風なやり方で二重送信問題を防ぐにはどうしたらいいのか調べてみた。
参考にした資料
PRGパターン(後述)について、wikipediaの記事と、その元になったTheServerSide(企業向けJavaアプリの開発者のためのポータルサイト?)の記事が参考になった。
Post/Redirect/Get - Wikipedia
Redirect After Post
また、NTTデータの「TERASOLUNA(テラソルナ)」というフレームワークのドキュメントが、RPGパターンを含めた対策について網羅的にカバーしている。
調べたこと
二重送信にはこんな種類がある。
更新完了後にブラウザ側で画面を再読み込み
更新完了後、戻るボタンを押してから再度更新リクエスト
更新リクエストを送信後、レスポンスが返ってくるまでに送信ボタンをもう一度押してしまう
更新リクエストを送信するページがブックマークされてしまい、画面遷移が正しくないためにデータの不整合が生じる
以下の対策が挙げられる。
- PRGパターン
- POSTリクエストを受け取ったら、更新完了などのページにリダイレクトする。
- 送信ボタンの非活性化
- 送信ボタンが一度押された時点でisClickedフラグを立て、レスポンスが返ってくるまでボタンが押せなくする。
- 更新リクエストを送信できる画面を返す際、サーバー側でトークンを発行しておく。クライアントは送信リクエストにそのトークンをくっつける。サーバー側で保管しておいたトークンと一致すればトランザクションを通す。一度使ったトークンはその場で破棄する。
- テーブルにタイムスタンプを持たせる
- 更新リクエストのタイムスタンプとテーブルの最終更新日時を比較し、テーブル側の方が新しければ更新しない。
誤りなどのご指摘があればぜひお願いします。
考えられること
サーバー側の不整合の防止と、クライアント側で誤って二重送信しないための工夫を分けて考えられそう。
トランザクショントークンでリクエストを検証することは必須とし、ユーザーが誤って更新しないようにボタンの非活性化やPRGパターンの導入をする・・・など。
まだ分かっていないこと
PRGパターンでブラウザバックが防げるのかわからない。資料によって言っていることが違う。ブラウザごとの挙動の問題?
PRGパターンを実装する上で、リダイレクトには実装方法がいくつかあるらしい。ページに埋め込む、httpのヘッダーに埋め込むなど・・・後者の方が良さそうだけど、どうなんだろう?