複数のフォーム送信を防ぐ一般的な方法を探しています。有望に見えるこのアプローチを見つけました。ただし、このスニペットをすべてのビューに含めたくありません。おそらく、リクエスト プロセッサまたはミドルウェアを使用してこれを行う方が簡単です。
ベストプラクティスの推奨事項はありますか?
クライアント側では、JavaScript から始めます。クライアントを信頼することはできませんが、それが始まりです。
すなわち
onclick="this.disabled=true,this.form.submit();
サーバー側では、データベースに何かを挿入することができます。つまり、チェックサムです。データベースに挿入するレコードがmodel.objects.get_or_create()
データベース レベルで一意性を強制するために使用する場合は、unique_together を使用する必要があります。
最後に: HTTPRedirect が最適です。ユーザーが支払いを処理するときに使用する方法は、HTTPRedirect() をサンキュー/確認ページに発行することです。この方法では、フォームを更新しても再送信されず、戻ってフォームを再送信しようとすると (フォームを更新せずに)、Django クロス サイト リクエスト フォージェリ (CSRF) は失敗します。完璧です!
また、ユーザーが送信ボタンをダブルクリックしたときに二重レコードが生成されるのを防ぐ良い方法を見つけようとしています。 リダイレクトによって簡単に修正できるのは、PRG の問題ではありません。
したがって、この基本的な懸念に関しては、サーバー側で HTTPRedirect を使用したソリューションは役に立ちません。
クライアント側では、送信前にボタンを無効にすると、2 つの問題が見つかりました。
form.submit()
、フォームが無効な場合、ブラウザーによって中断されます => 送信ボタンがまだdisabled=true
.disabled=true
ます。したがって、最初のクライアント側の問題 (HTML5 検証) に対する私の回避策は次のとおりです。
isFormHtml5Valid(form) {
for(var el of form.querySelectorAll('input,textarea,select')){
if(!el.checkValidity())
return false;
}
return true;
}
mySubmitButton.onclick = function() {
if(this.form && isFormHtml5Valid(this.form))
this.disabled=true;
this.form.submit();
}
2 番目のクライアント側の問題 (ブラウザーが DOM をキャッシュする) に対するクライアント側の回避策を見つけようとしましたが、何も機能しませんでした (onbeforeunload など)。したがって、私が現在「ブラウザキャッシュ」の問題に使用している回避策は、関連するビューの上部に @never_cache 装飾を追加することです (サーバー側から、クライアント側にキャッシュしないように指示します)。より良い回避策があれば教えてください。
最後になりましたが、サーバー側でこの問題を修正していただければ幸いです。CSRFトークンはセッションごとに(フォームごとではなく)生成されるため、CSRFソリューションは適切ではないようです。ここに私の仕事の状況と私の質問があります:
そのための良い解決策があれば教えてください。
編集1: 答えのごく一部かもしれません:シンクロナイザー(またはデジャヴ)トークン
しかし、Django ではその実装が見つかりませんでした。