【问题标题】:How do i include the csrf_token to dropzone Post request (Django)我如何将 csrf_token 包含到 dropzone Post 请求(Django)
【发布时间】:2017-02-08 02:58:21
【问题描述】:

好的,这已经解决了。只是编辑以防万一有人遇到同样的问题。

在同一 javascript 文件中添加标记为答案的评论中发布的代码。 定义时

var myDropzone =  new Dropzone(...
  ...//More stuff here
  headers:{
    'X-CSRFToken' : csrftoken
  }

就是这样。

所以当我通过 dropzone.js 向 django 提交 POST 请求时,我得到了 403 Forbidden。Django 显示消息说我没有包含 CSRF 令牌,但是如果我不使用HTML 中的表单。

document_form.html

{% extends 'base.html'  %}
{% load staticfiles %}
{% block title %}Add files{% endblock %}
{% block files %}
<div class="container-fluid" id="container-dropzone">
<div id="actions" class="row">

  <div class="col-lg-7">
    <span class="btn btn-success file-input-button">
      <i class="glyphicon glyphicon-plus"></i>
      <span>Add files...</span>
    </span>
    <button type="submit" class="btn btn-primary start">
      <i class="glyphicon glyphicon-upload"></i>
      <span>Start upload</span>
    </button>
    <button type="reset" class="btn btn-warning cancel">
      <i class="glyphicon glyphicon-ban-circle"></i>
      <span>Cancel upload</span>
    </button>
  </div>

  <div class="col-lg-5">
  <!-- file processing state -->
    <span class="fileupload-process">
      <div id="total-progress" class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
        <div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
      </div>
    </span>
  </div>
</div>

<div class="table table-striped files" id="previews">
  <div id="template" class="file-row">
    <div>
      <span class="preview"><img data-dz-thumbnail></span>
    </div>
    <div>
      <p class="name" data-dz-name></p>
      <strong class="error text-danger" data-dz-errormessage></strong>
    </div>
    <div>
      <p class="size" data-dz-size></p>
      <div class="progress progress-striped active" role="progressbar" aria-valuemin="0"
                 aria-valuemax="100" aria-valuenow="0">
        <div class="progress-bar progress-bar-success" style="width:0%" 
                    data-dz-uploadprogress>
        </div>           
      </div>
    </div>
    <div>
      <button class="btn btn-primary start">
        <i class="glyphicon glyphicon-upload"></i>
        <span>Start</span>
      </button>
      <button data-dz-remove class="btn btn-warning cancel">
        <i class="glyphicon glyphicon-ban-circle"></i>
        <span>Cancel</span>
      </button>
      <button data-dz-remove class="btn btn-danger delete">
        <i class="glyphicon glyphicon-trash"></i>
        <span>Delete</span>
      </button>
    </div>
  </div> <!-- /table-striped  -->
</div> <!-- /container-fluid  -->
</div>
{% endblock %}
{% block dz-add %}
  <script src="{% static 'js/dropzone-bootstrap.js' %}"></script>
{% endblock %}

dropzone-bootstrap.js

$(function() {
  var previewNode = document.querySelector("#template");
  previewNode.id = "";
  var previewTemplate = previewNode.parentNode.innerHTML;
  previewNode.parentNode.removeChild(previewNode);

  var myDropzone = new Dropzone(document.querySelector("#container-dropzone") , {
    url: "/dashby/files/add/", //url to make the request to.
    thumbnailWidth: 80,
    thumbnailHeight: 80,
    parallelUploads: 20,
    previewTemplate: previewTemplate,
    autoQueue: false,
    previewsContainer: "#previews",
    clickable: ".file-input-button",
    headers: { // Tried to apply the token this way but no success.
      'X-CSRFToken': $('meta[name="token"]').attr('content')
    }
  });

  myDropzone.on("addedfile", function(file){
    file.previewElement.querySelector(".start").onclick = function(){
      myDropzone.enqueueFile(file);
      };
  });

  myDropzone.on("totaluploadprogress", function(progress){
    document.querySelector("#total-progress .progress-bar").style.width = progress + "%";
  });

  myDropzone.on("sending", function(file){
    // Show total progress on start and disable START button.
    document.querySelector("#total-progress").style.opacity = "1";
    file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
  });

  // Hide progress bar when complete.
  myDropzone.on("queuecomplete", function(progress){
    document.querySelector("#total-progress").style.opacity = "0";
  });

  // Setup buttons for every file.
  document.querySelector("#actions .start").onclick = function(){
    myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
  };
  document.querySelector("#actions .cancel").onclick = function(){
    myDropzone.removeAllFiles(true);
  };
});

在我的 base.html 中添加所有必需的文件(dropzone、jquery、bootstrap 和我的自定义 javascript 文件)

对于django表单处理:

views.py

class DocumentCreate(CreateView):
    model = Document
    fields = ['file']
    def form_valid(self, form):
        self.object = form.save()
        data = {'status': 'success'}
        response = JSONResponse(data, mimetype =
        response_mimetype(self.request))
        return response

我的“文档”模型

class Document(models.Model):
    file = models.FileField(upload_to = 'files/',
                                validators=[validate_file_type])
    uploaded_at = models.DateTimeField(auto_now_add = True)
    extension = models.CharField(max_length = 30, blank = True)
    thumbnail = models.ImageField(blank = True, null = True)

    def clean(self):
        self.file.seek(0)
        self.extension = self.file.name.split('/')[-1].split('.')[-1]
        if self.extension == 'xlsx' or self.extension == 'xls':
            self.thumbnail = 'xlsx.png'
        elif self.extension == 'pptx' or self.extension == 'ppt':
            self.thumbnail = 'pptx.png'
        elif self.extension == 'docx' or self.extension == 'doc':
            self.thumbnail = 'docx.png'

    def delete(self, *args, **kwargs):
        #delete file from /media/files
        self.file.delete(save = False)
        #call parent delete method.
        super().delete(*args, **kwargs)

    #Redirect to file list page.
    def get_absolute_url(self):
        return reverse('dashby-files:files')

    def __str__(self):
        #cut the 'files/'
        return self.file.name.split('/')[-1]

    class Meta():
        #order by upload_date descending
        #for bootstrap grid system. (start left side)
        ordering = ['-uploaded_at']

我创建了一个 Json 响应来处理 dropzone。

response.py

from django.http import HttpResponse
import json

MIMEANY = '*/*'
MIMEJSON = 'application/json'
MIMETEXT = 'text/plain'

# Integrating Dropzone.js with Django.
def response_mimetype(request):
    can_json = MIMEJSON in request.META['HTTP_ACCEPT']
    can_json |= MIMEANY in request.META['HTTP_ACCEPT']
    return MIMEJSON if can_json else MIMETEXT

# Custom HttpResponse
class JSONResponse(HttpResponse):
    def __init__(self, obj='', json_opts=None, mimetype=MIMEJSON,
                *args, **kwargs):
        json_opts = json_opts if isinstance(json_opts, dict) else {}
        content = json.dumps(obj, **json_opts)
        super(JSONResponse, self).__init__(content, mimetype,
                                          *args, **kwargs)

我已经被这个问题困扰了一天,所以决定在这里寻求帮助,因为我一直找不到。

感谢任何花时间阅读的人以及我可以获得的任何帮助/提示。

【问题讨论】:

    标签: javascript jquery python django dropzone.js


    【解决方案1】:

    只需将{% csrf_token %} 放在您的 Html 文件中的任何位置。它会自动添加

     <input type="hidden" name="csrfmiddlewaretoken" value="**************" />
    

    在向服务器发送数据之前,只需添加额外的字段csrf_token,其值为$("input[name='csrfmiddlewaretoken']").val();

    【讨论】:

      【解决方案2】:

      我刚刚想通了,您只需将其添加到您的 dropzone 配置中:

      headers: {'X-CSRFToken': '{{ csrf_token }}'},
      

      祝你好运!

      【讨论】:

      • 就是这个。简单而有效™。
      【解决方案3】:

      Django 的文档有一个reference for this:

      虽然 [一个特殊参数] 可以用于 AJAX POST 请求,但它有一些不便之处:您必须记住在每个 POST 请求中将 CSRF 令牌作为 POST 数据传递。为此,有一种替代方法:在每个XMLHttpRequest 上,将自定义X-CSRFToken 标头设置为CSRF 令牌的值。这通常更容易,因为许多 JavaScript 框架提供了允许在每个请求上设置标头的钩子。
      […]

      获取令牌很简单:

      // using jQuery
      function getCookie(name) {
          var cookieValue = null;
          if (document.cookie && document.cookie !== '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = jQuery.trim(cookies[i]);
                  // Does this cookie string begin with the name we want?
                  if (cookie.substring(0, name.length + 1) === (name + '=')) {
                      cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                      break;
                  }
              }
          }
          return cookieValue;
      }
      var csrftoken = getCookie('csrftoken');
      

      […] 最后,您必须在 AJAX 请求中实际设置标头,同时在 jQuery 1.5.1 及更高版本中使用 settings.crossDomain 保护 CSRF 令牌不被发送到其他域:

      function csrfSafeMethod(method) {
          // these HTTP methods do not require CSRF protection
          return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
      }
      $.ajaxSetup({
          beforeSend: function(xhr, settings) {
              if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                  xhr.setRequestHeader("X-CSRFToken", csrftoken);
              }
          }
      });
      

      如果您在开始发出请求之前运行这两个代码块,它应该是 Just Work™。

      总而言之,只要使用这个代码块:

      // from https://docs.djangoproject.com/en/1.10/ref/csrf/ via http://stackoverflow.com/a/39776325/5244995.
      function getCookie(name) {
          var cookieValue = null;
          if (document.cookie && document.cookie !== '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = jQuery.trim(cookies[i]);
                  // Does this cookie string begin with the name we want?
                  if (cookie.substring(0, name.length + 1) === (name + '=')) {
                      cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                      break;
                  }
              }
          }
          return cookieValue;
      }
      var csrftoken = getCookie('csrftoken');
      
      function csrfSafeMethod(method) {
          // these HTTP methods do not require CSRF protection
          return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
      }
      $.ajaxSetup({
          beforeSend: function(xhr, settings) {
              if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                  xhr.setRequestHeader("X-CSRFToken", csrftoken);
              }
          }
      });
      

      【讨论】:

      • 我将所有这些代码放在我的 javascript 文件中,放在 dropzone 的 $(function(){}); 逻辑之前,但我仍然收到错误。我需要在某个地方专门调用它吗?
      • 好吧想通了。在 dropzone 配置中,我使用 cookie 作为标头的值。非常感谢
      【解决方案4】:

      虽然这是一个旧线程,但我想我会分享对我有用的解决方案。

      var myDropzone = $("#file-upload").dropzone({
              url: "/dashboard/api/v1/upload/",
              // addRemoveLinks : true,
              maxFilesize: 5,
              dictResponseError: 'Error uploading file!',
              headers: {'X-CSRFToken': window.CSRF_TOKEN},
              success: (file, response) => {
                  console.log(JSON.parse(file.xhr.response));
              }
          });
      

      【讨论】:

        【解决方案5】:

        docs 建议从 cookie 而不是 DOM 中获取 CSRF 令牌。试试看。

        【讨论】:

        • 你能帮我实现吗?我阅读了文档,但我不确定在哪里做。
        猜你喜欢
        • 2015-07-25
        • 2011-04-30
        • 2018-10-29
        • 1970-01-01
        • 2015-07-20
        • 1970-01-01
        • 2016-04-24
        • 2022-12-03
        • 1970-01-01
        相关资源
        最近更新 更多