Django CSRF check failing with an Ajax POST request

前端 未结 22 1407
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-22 03:46

I could use some help complying with Django\'s CSRF protection mechanism via my AJAX post. I\'ve followed the directions here:

http://docs.djangoproject.com/en/dev/r

相关标签:
22条回答
  • 2020-11-22 03:47

    Add this line to your jQuery code:

    $.ajaxSetup({
      data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
    });
    

    and done.

    0 讨论(0)
  • 2020-11-22 03:47

    Easy ajax calls with Django

    (26.10.2020)
    This is in my opinion much cleaner and simpler than the correct answer.

    The view

    @login_required
    def some_view(request):
        """Returns a json response to an ajax call. (request.user is available in view)"""
        # Fetch the attributes from the request body
        data_attribute = request.GET.get('some_attribute')  # Make sure to use POST/GET correctly
        # DO SOMETHING...
        return JsonResponse(data={}, status=200)
    

    urls.py

    urlpatterns = [
        path('some-view-does-something/', views.some_view, name='doing-something'),
    ]
    

    The ajax call

    The ajax call is quite simple, but is sufficient for most cases. You can fetch some values and put them in the data object, then in the view depicted above you can fetch their values again via their names.

    You can find the csrftoken function in django's documentation. Basically just copy it and make sure it is rendered before your ajax call so that the csrftoken variable is defined.

    $.ajax({
        url: "{% url 'doing-something' %}",
        headers: {'X-CSRFToken': csrftoken},
        data: {'some_attribute': some_value},
        type: "GET",
        dataType: 'json',
        success: function (data) {
            if (data) {
                console.log(data);
                // call function to do something with data
                process_data_function(data);
            }
        }
    });
    

    Add HTML to current page with ajax

    This might be a bit off topic but I have rarely seen this used and it is a great way to minimize window relocations as well as manual html string creation in javascript.

    This is very similar to the one above but this time we are rendering html from the response without reloading the current window.

    If you intended to render some kind of html from the data you would receive as a response to the ajax call, it might be easier to send a HttpResponse back from the view instead of a JsonResponse. That allows you to create html easily which can then be inserted into an element.

    The view

    # The login required part is of course optional
    @login_required
    def create_some_html(request):
        """In this particular example we are filtering some model by a constraint sent in by 
        ajax and creating html to send back for those models who match the search"""
        # Fetch the attributes from the request body (sent in ajax data)
        search_input = request.GET.get('search_input')
    
        # Get some data that we want to render to the template
        if search_input:
            data = MyModel.objects.filter(name__contains=search_input) # Example
        else:
            data = []
    
        # Creating an html string using template and some data
        html_response = render_to_string('path/to/creation_template.html', context = {'models': data})
    
        return HttpResponse(html_response, status=200)
    

    The html creation template for view

    creation_template.html

    {% for model in models %}
       <li class="xyz">{{ model.name }}</li>
    {% endfor %}
    

    urls.py

    urlpatterns = [
        path('get-html/', views.create_some_html, name='get-html'),
    ]
    

    The main template and ajax call

    This is the template where we want to add the data to. In this example in particular we have a search input and a button that sends the search input's value to the view. The view then sends a HttpResponse back displaying data matching the search that we can render inside an element.

    {% extends 'base.html' %}
    {% load static %}
    {% block content %}
        <input id="search-input" placeholder="Type something..." value="">
        <button id="add-html-button" class="btn btn-primary">Add Html</button>
        <ul id="add-html-here">
            <!-- This is where we want to render new html -->
        </ul>
    {% end block %}
    
    {% block extra_js %}
        <script>
            // When button is pressed fetch inner html of ul
            $("#add-html-button").on('click', function (e){
                e.preventDefault();
                let search_input = $('#search-input').val();
                let target_element = $('#add-html-here');
                $.ajax({
                    url: "{% url 'get-html' %}",
                    headers: {'X-CSRFToken': csrftoken},
                    data: {'search_input': search_input},
                    type: "GET",
                    dataType: 'html',
                    success: function (data) {
                        if (data) {
                            /* You could also use json here to get multiple html to
                            render in different places */
                            console.log(data);
                            // Add the http response to element
                            target_element.html(data);
                        }
                    }
                });
            })
        </script>
    {% endblock %}
    
    0 讨论(0)
  • 2020-11-22 03:47

    Here's a less verbose solution provided by Django:

    <script type="text/javascript">
    // using jQuery
    var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
    
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    // set csrf header
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
    
    // Ajax call here
    $.ajax({
        url:"{% url 'members:saveAccount' %}",
        data: fd,
        processData: false,
        contentType: false,
        type: 'POST',
        success: function(data) {
            alert(data);
            }
        });
    </script>
    

    Source: https://docs.djangoproject.com/en/1.11/ref/csrf/

    0 讨论(0)
  • 2020-11-22 03:48

    You can paste this js into your html file, remember put it before other js function

    <script>
      // 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;
      }
    
      function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
      }
    
      $(document).ready(function() {
        var csrftoken = getCookie('csrftoken');
        $.ajaxSetup({
          beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
              xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
          }
        });
      });
    </script>
    
    0 讨论(0)
  • 2020-11-22 03:50

    Related to the chosen Answer, just want to add on to the chosen Answer.

    In that answer, regarding the solution with .ajaxSetup(...). In your Django settings.py, if you have

    CSRF_USE_SESSIONS = True
    

    It would cause the chosen Answer to not work at all. Deleting that line, or setting it to False worked for me while implementing the chosen Answer's solution.

    Interestingly, if you set the following in your Django settings.py

    CSRF_COOKIE_HTTPONLY = True
    

    This variable will not cause the chosen Answer's solution to stop functioning.

    Both CSRF_USE_SESSIONS and CSRF_COOKIE_HTTPONLY comes from this official Django doc https://docs.djangoproject.com/en/2.2/ref/csrf/

    (I do not have enough rep to comment, so I am posting my inputs an Answer)

    0 讨论(0)
  • 2020-11-22 03:51

    One CSRF token is assigned to every session ( i.e. every time you log in). So before you wish to get some data entered by user and send that as ajax call to some function which is protected by csrf_protect decorator, try to find the functions that are being called before you are getting this data from user. E.g. some template must be being rendered on which your user is entering data. That template is being rendered by some function. In this function you can get csrf token as follows: csrf = request.COOKIES['csrftoken'] Now pass this csrf value in context dictionary against which template in question is being rendered. Now in that template write this line: Now in your javascript function, before making ajax request, write this: var csrf = $('#csrf').val() this will pick value of token passed to template and store it in variable csrf. Now while making ajax call, in your post data, pass this value as well : "csrfmiddlewaretoken": csrf

    This will work even if you are not implementing django forms.

    In fact, logic over here is : You need token which you can get from request. So you just need to figure out the function being called immediately after log in. Once you have this token, either make another ajax call to get it or pass it to some template which is accessible by your ajax.

    0 讨论(0)
提交回复
热议问题