Запрещен HTTP 403 DRF + ReactJS request.session

Я работаю над приложением викторины, используя DjangoRestFramework и ReactJS. Мое приложение состоит из двух приложений, api и frontend. В моем приложении api у меня есть несколько представлений API для разных вещей. Одно из моих представлений API называется JoinQuiz. Когда я вызываю его на моем frontend, я получаю эту ошибку в консоли:

Forbidden: /api/join-quiz
[16/Dec/2020] "POST /api/join-quiz HTTP/1.1" 403 58

Я не думаю, что моя проблема связана с ошибкой CSRF, потому что другие мои представления API работают отлично. Я могу ошибаться в этом вопросе.

[16/Dec/2020] "GET /api/get-question?id=3 HTTP/1.1" 200 10009 
[17/Dec/2020 01:15:47] "POST /api/create-quiz HTTP/1.1" 201 11959

Я подозреваю, что мой request.session может быть проблемой, потому что, когда я иду прямо к /api/join-quiz и делаю запрос POST с моим code, ничего не происходит, и у меня есть успешный пост.

Файлы

Views.py

class QuizView(generics.ListAPIView):
    queryset = Quiz.objects.all()
    serializer_class = QuizSerializer

class GetQuiz(APIView):
    """ Searches for a quiz given its code and returns the Quiz with is_host info"""
    serializer_class = QuizSerializer
    lookup_url_kwarg = 'code'

    def get(self, request, format=None): # This is an HTTP GET request
        code = request.GET.get(self.lookup_url_kwarg)
        if code != None: # Check if code is not equal to None
            quiz = Quiz.objects.filter(code=code)
            if len(quiz) > 0: # If there is a quiz...
                data = QuizSerializer(quiz[0]).data
                data['is_host'] = self.request.session.session_key == quiz[0].host
                

                return Response(data, status=status.HTTP_200_OK)

            return Response({'Quiz not found': 'Invalid Code'}, status=status.HTTP_404_NOT_FOUND)
        return Response({'Bad Request': 'Code Parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
        

        
class CreateQuizView(APIView):
    """Creates A new Quiz given nested question and answer data"""
    serializer_class = QuizSerializer

    def post(self, request, format=None):
        """ Create the User's Account first"""
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()
        
        data = request.data
        data['host'] = self.request.session.session_key
        serializer = self.serializer_class(data=data)

        if serializer.is_valid():
            
            quiz = serializer.create(validated_data=data)
            self.request.session['quiz_code'] = quiz.code

            return Response(
                self.serializer_class(quiz).data, 
                status=status.HTTP_201_CREATED
                )


        return Response({'Bad Request': 'Invalid Data'}, status=status.HTTP_400_BAD_REQUEST)

        
class JoinQuiz(APIView):
    """Join a quiz based on the quiz code"""
    lookup_url_kwarg = 'code'

    def post(self, request, format=None):
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()

        print(self.request.session.session_key)
        code = request.data.get(self.lookup_url_kwarg)

        if code != None:
            quiz_result = Quiz.objects.filter(code=code)
            if len(quiz_result) > 0:

                self.request.session['quiz_code'] = code
                return Response({'message': 'Quiz Joined!'}, status=status.HTTP_200_OK)
            
            return Response({'Quiz Not Found': 'Invalid Quiz Code'}, status=status.HTTP_404_NOT_FOUND)
        
        return Response({'Bad Request': 'Invalid Post Data'}, status=status.HTTP_400_BAD_REQUEST)
        

Serializers.py




class QuizSerializer(serializers.ModelSerializer):
    
    questions = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Quiz
        fields = ['id', 'code', 'questions', 'name']

    def create(self, validated_data):
        questions_data = validated_data.pop('questions')

        quiz = Quiz.objects.create(**validated_data)

        for question_data in questions_data:
            
            answers_data = question_data.pop('answers')
            question = Question.objects.create(quiz=quiz, **question_data)

            for answer_data in answers_data: 
                Answer.objects.create(question=question, **answer_data)

        return quiz

Внешний запрос

  const quizButtonPressed = () => {
      const requestOptions = {
          method: "POST",
          headers: {"Content-Type": "application/json"},
          body: JSON.stringify({
              code: quizCode
          })
      };
      fetch('/api/join-quiz', requestOptions)
      .then((response) => {
          if (response.ok) {
              props.history.push(`/play/${quizCode}`);
          } else {
              setError("Quiz not found")
          }        
      })
      .catch((error) => console.info(error));
  }

РЕДАКТИРОВАТЬ

Я следовал решению @Blusky, и оно сработало!

Перейдя по ссылке на документы Django от @Blusky, проблема решена: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
466
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Даже если вы проверили это, это может быть проблема CSRF. Вы должны проверить тело ошибки 403, оно может содержать дополнительную информацию.

Только при аутентификации запрос POST на Django требует токена CSRF (возможно, поэтому ваш первый POST работает)

Если это так, вы можете проверить этот фрагмент: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

Это сработало! Большое спасибо, я просто предположил, что CSRF не имеет отношения к моей проблеме, потому что он работал для другого моего запроса. Ссылка на документы Django очень помогла при внедрении токена CSRF.

Rayyan 18.12.2020 23:43

Другие вопросы по теме