10

我正在寻找一种通用的方法来防止多次提交表单。我发现这种方法看起来很有希望。而我不想在我的所有视图中包含这个片段。使用请求处理器或中间件可能更容易做到这一点。

有什么最佳实践建议吗?

4

4 回答 4

21

客户端,从 JavaScript 开始。你永远不能相信客户,但它是一个开始。

IE

onclick="this.disabled=true,this.form.submit();

服务器端您“可以”将某些内容插入数据库,即校验和。如果它是您插入数据库的记录,model.objects.get_or_create()用于强制数据库级别的唯一性,您应该使用 unique_together。

最后:HTTPRedirect 是最好的,我在用户处理付款时使用的方法是向感谢/确认页面发出 HTTPRedirect()。这种方式刷新表单不会重新提交,如果他们返回并尝试再次提交表单(不刷新表单),Django Cross Site Request Forgery (CSRF) 将失败,完美!

于 2013-03-27T23:23:01.083 回答
3

我还尝试找到一种好方法来防止在用户 dbl-click 提交按钮时生成双重记录。 这与通过重定向很容易解决的 PRG 问题无关。

因此,关于这个基本问题,在服务器端使用 HTTPRedirect 的解决方案没有帮助。

在客户端,我在提交前禁用按钮时发现了两个问题:

  1. 使用 HTML5 验证,form.submit()如果表单无效 => 提交按钮仍然是 ,浏览器将中断disabled=true
  2. 当用户提交表单并返回浏览器历史记录时,DOM 将从浏览器的缓存中加载 => 提交按钮仍然是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();
}

我尝试为第二个客户端问题(浏览器缓存 DOM)找到客户端解决方法,但没有任何效果(onbeforeunload,...)。所以我目前用于“浏览器缓存”问题的解决方法是在相关视图的顶部添加一个@never_cache 装饰(从服务器端,指示客户端不缓存)。如果您有更好的解决方法,请告诉我。

最后但并非最不重要的一点是,我非常感谢在服务器端解决此问题。CSRF 解决方案似乎不适合,因为CSRF 令牌是由会话生成的(不是针对每个表单)。所以这是我的工作状态和我的问题:

  • 在客户端解决此问题是可以的,但对我来说似乎不是一个好的解决方案。我们如何避免在服务器端验证这种多表单提交?

让我知道你是否有一个好的解决方案。


编辑1: 可能是答案的一小部分:Synchronizer (or Déjà vu) Token

但我没有在 Django 中找到任何实现。

于 2016-09-07T11:06:30.157 回答
1

使用HttpResponseRedirect

创建一个新视图(让我们说thank_you)以在表单提交后显示成功消息并返回模板。

成功提交表单后,将 HttpResponseRedirect("/thank-you/")返回到新的感谢视图

from django.http import HttpResponseRedirect

def thank_you(request, template_name='thank-you.html'):
    return render_to_response(template_name,locals(),context_instance=RequestContext(request))

并在 urls.py

url(r'^thank-you/$','thank_you', name="thank_you")

发生多个表单提交是因为当页面刷新相同的 url 命中时,它会一次又一次地调用相同的视图,因此在数据库中保存了多个条目。为了防止这种情况,我们需要将响应重定向到新的 url/view,以便下次页面刷新时它会点击新的 url/view。

于 2017-03-15T06:56:18.460 回答
0
  1. 成功提交后以重定向响应,因此重新加载不会导致再次提交
  2. 禁用表单提交时的提交按钮

至于#2,最好在onsubmit处理程序中执行此操作,而不是onclick提交按钮。这将处理多个提交按钮或任何 HTML5 客户端表单验证等情况。在 jQuery 中,它看起来像:

$('#edit_form').submit( function(event) {
    // disable to avoid double submission
    $('#submit_button').attr('disabled', true);
});

但是,这没有考虑的一个问题是用户是否中途取消了提交。例如,如果您单击“提交”,然后立即按“Esc”,浏览器将停止提交,但“提交”按钮已被禁用。

此外,可以通过点击“输入”来提交表单,并且此解决方案不会阻止该表单提交两次。

于 2017-12-12T15:03:42.187 回答