2

Based on the w3schools ajax example I am trying to make a delete call and then remove the corresponding row from a table. There are plenty of answers here about how to do it using JQuery but I am not doing that. I found this answer which made me write my JavaScript like this:

function deleteFullLicense(rowid, objectid) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (xhttp.readyState == 4 && xhttp.status == 204) {
        row = document.getElementById(rowid);
        row.parentNode.removeChild(row);
      }
      else {
        window.alert("Something went wrong. The delete failed.");
      }
    };
    xhttp.open("POST", "deleteLicense/" + objectid, true);
    xhttp.send({'csrfmiddlewaretoken': '{{ csrf_token }}'});
}

But I get the Forbidden (CSRF token missing or incorrect.) message. How should I send the token?

Community
  • 1
  • 1
jonalv
  • 5,706
  • 9
  • 45
  • 64
  • 1
    You need to set is as a header. Maybe this can help you: http://stackoverflow.com/questions/22063612/adding-csrftoken-to-ajax-request – rubentd Apr 25 '16 at 15:00
  • I have tried to set it as header using: `xhttp.setRequestHeader("csrfmiddlewaretoken", '{{ csrf_token }}')` but that makes no difference. Is there something more that needs to be done? – jonalv Apr 25 '16 at 15:31

2 Answers2

3

Turns out if I called it X-CSRFToken instead it worked. Found out about it here if you want to read more.

function deleteFullLicense(rowid, objectid) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (xhttp.readyState == 4 && xhttp.status == 204) {
        row = document.getElementById(rowid);
        row.parentNode.removeChild(row);
      }
    };
    xhttp.open("POST", "deleteLicense/" + objectid, true);
    xhttp.setRequestHeader("X-CSRFToken", '{{ csrf_token }}')
    xhttp.send();
}
Andy Swift
  • 2,179
  • 3
  • 32
  • 53
jonalv
  • 5,706
  • 9
  • 45
  • 64
1

The header name X-CSRFToken actually comes from the parameter CSRF_HEADER_NAME in Django settings.py. When receiving frontend request (e.g. ajax call), Django internally checks header parameters and converts X-CSRFToken to HTTP_X_CSRFTOKEN which is default value of CSRF_HEADER_NAME .

The better approach would be to :

  • convert the value of CSRF_HEADER_NAME
  • render the converted value in previous step, to the HTML template
  • in the frontend code (e.g. the HTML template or separate js file), create a custom header with that value and the CSRF token on each ajax call for form submission.

Here's a quick example :

In settings.py

CSRF_HEADER_NAME = "HTTP_ANTI_CSRF_TOKEN"

In the view function of views.py

from django.conf import settings
from django.http.request import HttpHeaders

prefix =  HttpHeaders.HTTP_PREFIX
converted = settings.CSRF_HEADER_NAME[len(prefix):]
converted = converted.replace('_','-')
# so the value HTTP_ANTI_CSRF_TOKEN is converted to ANTI-CSRF-TOKEN,
return Response(context={'custom_csrf_header_name':converted})

In your HTML template (not good practice, since this is just quick example)

<script>
// Note that the value is 'ANTI-CSRF-TOKEN'. when this header name goes to
// backend server, Django will internally convert it back to 'HTTP_ANTI_CSRF_TOKEN'
var custom_csrf_header_name = "{{ custom_csrf_header_name }}";

// the ajax part is almost the same as described in the accepted answer
...
xhttp.setRequestHeader(custom_csrf_header_name, '{{ csrf_token }}')
...
</script>
Ham
  • 703
  • 8
  • 17