简体   繁体   中英

403 Forbidden error when making an ajax Post request in Django framework

I am trying to integrate jquery into a web application I am making with Django framework. I am however having a hard time trying to make a simple ajax call to work. My template file that contains the form html and javascript to handle the ajax call looks like:

<script type="text/javascript">
$(document).ready(function() {
$( "#target" ).submit(function() {
console.log('Form was submitted');
$.ajax({
        type: "POST",
        url: "/hello/",  // or just url: "/my-url/path/"
        data: {
            query: $( "#query" ).val()   
        },
        success: function(data) {
            console.log(data);
        }
    });
return false;
  });   
  })
</script>
<form id="target" action="." method="post">{% csrf_token %}
 <input id= "query" type="text" value="Hello there">
 <input type="submit" value="Search Recent Tweets">
</form>

My views.py that is supposed to handle the ajax call looks like:

 from django.core.context_processors import csrf
 from django.shortcuts import render_to_response
 from django.template.loader import get_template
 from django.template import Context,RequestContext
 from django.views.decorators.csrf import ensure_csrf_cookie
 from django.http import HttpResponse

 # access resource
 def hello(request):
  c = {}
  c.update(csrf(request))
  if request.is_ajax():
        t = get_template('template.html')
        #html = t.render(Context({'result': 'hello world'}))
        con = RequestContext(request, {'result': 'hello world'})
        return render_to_response('template.html', c, con)
  else:
        return HttpResponse('Not working!') 

I have tried to follow the official documentation on Cross-Site Request Forgery Protection and also looked at several stackoverflow questions addressing a similar problem. I have included the {% csrf_token %} in my html template file but it still doesn't seem to be working. I get an error in the console suggesting that the ajax call failed:

POST http://127.0.0.1:8000/hello/ 403 (FORBIDDEN)   

How do I pass the result variable along with my http response and get the ajax call to work smoothly? Any help is deeply appreciated.

Edit-1

I wasn't supposedly passing the csrf token along with my post request. SO as per the documentation I added the following code to my template javascript:

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');
console.log(csrftoken);

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

When I refresh the template html page in the browser, I get null in the console, suggesting that the cookie is not set or not defined. What am I missing?

Because you did not post the csrfmiddlewaretoken , so Django forbid you.this document can help you.

For the lazy guys:

First download cookie: http://plugins.jquery.com/cookie/

Add it to your html:

<script src="{% static 'designer/js/jquery.cookie.js' %}"></script>

Now you can create a working POST request:

var csrftoken = $.cookie('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);
        }
    }
});

$.ajax(save_url, {
    type : 'POST',
    contentType : 'application/json',
    data : JSON.stringify(canvas),
    success: function () {
        alert("Saved!");
    }

})

The fastest solution if you are not embedding js into your template is:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

If you do embed your js into the template, it's as simple as: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

I find all previous answers on-spot but let's put things in context.

The 403 forbidden response comes from the CSRF middleware (see Cross Site Request Forgery protection ):

By default, a '403 Forbidden' response is sent to the user if an incoming request fails the checks performed by CsrfViewMiddleware.

Many options are available. I would recommend to follow the answer of @fivef in order to make jQuery add the X-CSRFToken header before every AJAX request with $.ajaxSetup .

This answer requires the cookie jQuery plugin. If this is not desirable, another possibility is to add:

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');

BUT: if the setting CSRF_COOKIE_HTTPONLY is set to True, which often happens as the Security middleware recommends so, then the cookie is not there, even if @ensure_csrf_cookie() is used. In this case {% csrf_token %} must be provided in every form, which produces an output such as <input name="csrfmiddlewaretoken" value="cr6O9...FUXf6" type="hidden"> . So the csrfToken variable would simply be obtained with:

var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();

Again $.ajaxSetup would be required of course.

Other options which are available but not recommended are to disable the middleware or the csrf protection for the specific form with @csrf_exempt() .

data: {"csrfmiddlewaretoken" : "{{csrf_token}}"}

You see "403 (FORBIDDEN)", because you don`t send "csrfmiddlewaretoken" parameter. In template each form has this: {% csrf_token %}. You should add "csrfmiddlewaretoken" to your ajax data dictionary. My example is sending "product_code" and "csrfmiddlewaretoken" to app "basket" view "remove":

$(function(){
    $('.card-body').on('click',function(){
        $.ajax({
          type: "post",
          url: "{% url 'basket:remove'%}",
          data: {"product_code": "07316", "csrfmiddlewaretoken" : "{{csrf_token}}" }
        });
    })
});

To set the cookie, use the ensure_csrf_cookie decorator in your view:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def hello(request):
    code_here()

Make sure you aren't caching the page/view that your form is showing up on. It could be caching your CSRF_TOKEN. Happened to me!

Another approach is to add X-CSRFTOKEN header with the "{{ csrf_token }}" value like in the following example:

$.ajax({
            url: "{% url 'register_lowresistancetyres' %}",
            type: "POST",
            headers: {//<==
                        "X-CSRFTOKEN": "{{ csrf_token }}"//<==
                },
            data: $(example_form).serialize(),
            success: function(data) {
                //Success code
            },
            error: function () {
                //Error code
            }
        });

Try including this decorator on your dispatch code

from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt     
@method_decorator(csrf_exempt, name='dispatch')
def dispatch(self, request, *args, **kwargs):
     return super(LessonUploadWorkView,self).dispatch(request,*args,**kwargs)

With SSL/https and with CSRF_COOKIE_HTTPONLY = False, I still don't have csrftoken in the cookie, either using the getCookie(name) function proposed in django Doc or the jquery.cookie.js proposed by fivef .

Wtower summary is perfect and I thought it would work after removing CSRF_COOKIE_HTTPONLY from settings.py but it does'nt in https!

Why csrftoken is not visible in document.cookie???

Instead of getting

"django_language=fr; csrftoken=rDrGI5cp98MnooPIsygWIF76vuYTkDIt"

I get only

"django_language=fr"

WHY? Like SSL/https removes X-CSRFToken from headers I thought it was due to the proxy header params of Nginx but apparently not... Any idea?

Unlike django doc Notes , it seems impossible to work with csrf_token in cookies with https. The only way to pass csrftoken is through the DOM by using {% csrf_token %} in html and get it in jQuery by using

var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();

It is then possible to pass it to ajax either by header ( xhr.setRequestHeader ), either by params .

this works for me

template.html

  $.ajax({
    url: "{% url 'XXXXXX' %}",
    type: 'POST',
    data: {modifica: jsonText, "csrfmiddlewaretoken" : "{{csrf_token}}"},
    traditional: true,
    dataType: 'html',
    success: function(result){
       window.location.href = "{% url 'XXX' %}";
        }
});

view.py

def aggiornaAttivitaAssegnate(request):
    if request.is_ajax():
        richiesta = json.loads(request.POST.get('modifica'))

您必须更改文件夹 chmod 755 和文件 (.php ,.html) chmod 644。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM