Django rest framework ajax form submit error 403 (forbidden)

Question:

When i try to submit my ajaxified form that’s working with the DRF API i get in the browser console!

POST http://localhost:8000/api/texts/ 403 (Forbidden)

here is my html file :

 <form id="text-form" method="POST" action="">
                   

                            <input type="text" name="title" placeholder="Title" class="form-control mb-3 pb-2"
                                maxlength="200" required id="title">
                            <input type="date" name="deadline" placeholder="Deadline" autocomplete="off"
                                class="form-control mb-3" id="myflatpickr">                                         
                      
                            <textarea name="requirements" cols="40" rows="4"
                                placeholder="requirements"
                                class="form-control col mt-3" maxlength="200" required id="requirements"></textarea>

                    <textarea name="document" cols="40" rows="10"
                        placeholder="document"
                        id="editor" class="form-control" required></textarea>

                <button type="submit">Submit</button>
                </form>

here is my javascript file

  $("#text-form").submit(function (event) {
    event.preventDefault();
    $textData = $("#text-form").serialize()
    $.ajax({
      url: "http://localhost:8000/api/texts/",
      method: "POST",
      data: $textData,
      success: function() {
        console.log($textData)
      },
      error: function() {
        console.log("there is an error")

      }
    })

  });

in serializers.py:


from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers
from .models import *



class TextSerializer(serializers.ModelSerializer):
     author = serializers.HiddenField(
         default=serializers.CurrentUserDefault()
     )
     class Meta:
         model = Text
         fields = '__all__'

in my views.py file:


class ApiTextList(generics.ListCreateAPIView):

    queryset = Text.objects.all()
    serializer_class = TextSerializer
    permission_classes = [
        permissions.AllowAny
    ]


class ApiTextDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):

    http_method_names = ['get', 'head']
    queryset = Text.objects.all()
    serializer_class = TextSerializer
    permission_classes = [
        permissions.AllowAny
    ]

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

in urls.py

from django.urls import path
from . import views


urlpatterns = [

    path('api/texts/', views.ApiTextList.as_view()),
    path('api/texts/<int:pk>/', views.ApiTextDetail.as_view()),
    
    
]

note: when i try to add a text from the interface that drf provides in "localhost:8000/api/texts" i add it normally

Asked By: Mohcen CH

||

Answers:

You are getting Error 403, which means the server understood the request but refuses to authorize it. You need to add an authorization token while sending an ajax request.

You can authorize a token on your page using this.

<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>

Then, you have to add the token to your ajax request.

$.ajax({
  url: "http://localhost:8000/api/texts/",
  method: "POST",
  data: $textData,

  csrfmiddlewaretoken: window.CSRF_TOKEN,

  success: function() {
    console.log($textData)
  },
  error: function() {
    console.log("there is an error")

  }
})
Answered By: Mhasan502

Now that you told us the content of the details field, it should be easier to fix your problem.

The Django documentation advises you to get the CSRF token from the cookies.

It even gives you the following function to do that:

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
}

And you can then easily adapt your own code by adding those two lines:

$("#text-form").submit(function (event) {
    event.preventDefault();
    const csrftoken = getCookie('csrftoken'); // HERE: get the token 
    $textData = $("#text-form").serialize()
    $.ajax({
        url: "http://localhost:8000/api/texts/",
        method: "POST",
        data: $textData,
        headers:{"X-CSRFToken": csrftoken }, // HERE: add it to the request header
        success: function() {
            console.log($textData)
        },
        error: function() {
            console.log("there is an error")
        }
    })
});

If that does not work, please check that you are correctly using Session Authentication.

And to answer your other interrogation, this is normal that your registration view works without the CSRF token: in the DRF, only the views where you need to be authenticated require it.

Answered By: Mickaël Martinez

try this, for Forbidden 403 :

$.ajax({
  url: "http://localhost:8000/api/texts/",
  method: "POST",
  data: $textData,
  contentType: "application/json;charset=utf-8",
  dataType: "json",
  headers: {"X-CSRFToken": "{{ csrf_token }}"},

  success: function() {
    console.log($textData)
  },
  error: function() {
    console.log("there is an error")
  }
})
Answered By: NourEldin Osama