Запрещено (токен CSRF отсутствует или неверен): с использованием Django и JS

Я делаю POST-запрос, используя Axios в моем JS-коде, чтобы отправить некоторую информацию на мой локальный сервер Django. У меня есть {% csrf_token%} в форме моего html-кода, но я не знаю, как отправить токен csrf с помощью Axios.

Я получаю эту ошибку в терминале: Запрещено (токен CSRF отсутствует или неверен.): / Api / scoring / submit_score_details.

Как мне правильно вставить токен csrf в мой пост axios? Сейчас я не думаю, что JS может читать {{csrf_token}} в том виде, в каком он есть у меня. Я искал Stack, но похоже, что большинство людей используют jQuery или какой-либо другой тип JS.

Чтобы сэкономить место, я не опубликовал, какие переменные в полезной нагрузке, но все они просто строки.

Я могу убрать ошибку, если поставлю @csrf_exempt над моей функцией в файле views.py.

{
let payload = {
    "csrfmiddlewaretoken": "{{ csrf_token }}",
    "math_problem": problem,
    "user_answer": userInput,
    "true_answer": correctAnswer,
    "question_status": questionStatus,
}
console.log(payload);
axios.post('../api/scoring/submit_score_details', payload)
}
<div class="col text-center">
    <button id="start" type="button" class="btn btn-primary btn-lg">
        New Problem
    </button>
    <p id="math_problem"></p>
    <form id="inputForm" method="POST">
        {% csrf_token %}
        <input id="user_input" autocomplete="off" class="form-control form-control-lg" type="text" placeholder="Type your answer here">
        <input id="correct_answer" type="hidden">
    </form>
    <br>
    <button id="result_check" type="button" class="btn btn-primary btn-lg">Check</button>
    <script src={% static 'js/game_logic.js' %}></script>
</div>

{
let payload = {
    "csrfmiddlewaretoken": "{{ csrf_token }}",
    "math_problem": problem,
    "user_answer": userInput,
    "true_answer": correctAnswer,
    "question_status": questionStatus,
}
console.log(payload);
axios.post('../api/scoring/submit_score_details', payload)
}
<form id="inputForm" method="POST">
    {% csrf_token %}
    <input id="user_input" autocomplete="off" class="form-control form-control-lg" type="text" placeholder="Type your answer here">
    <input id="correct_answer" type="hidden">
</form>


person Hiebs915    schedule 09.12.2020    source источник


Ответы (3)


В итоге я погуглил и спросил друга. Мы нашли решение.

Чтобы все заработало, нам пришлось добавить две строчки кода:

axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';

Мы также избавились от переменной payload и просто поместили все в код Axios.

{
        axios.defaults.xsrfCookieName = 'csrftoken';
        axios.defaults.xsrfHeaderName = 'X-CSRFToken';
        axios.post('../api/scoring/submit_score_details', {
            "math_problem": problem,
            "user_answer": userInput,
            "true_answer": correctAnswer,
            "question_status": questionStatus,
        });
        console.log(`Problem:${problem},
                     User Input: ${userInput},
                     Correct Answer: ${correctAnswer},
                     Question Status: ${questionStatus}`
                     );
    };
<div class="col text-center">
    <button id="new_problem_button" type="button" class="btn btn-primary btn-lg">
        New Problem
    </button>
    <p id="math_problem"></p>
    <form id="inputForm" method="POST">
        {% csrf_token %}
        <input id="user_input" autocomplete="off" class="form-control form-control-lg" type="text" placeholder="Type your answer here">
        <input id="correct_answer" type="hidden">
    </form>
    <br>
    <button id="result_check" type="button" class="btn btn-primary btn-lg">Check</button>
    <script src={% static 'js/game_logic.js' %}></script>
</div>

Вот ссылка, которая нам помогла.

person Hiebs915    schedule 10.12.2020

Возможно ли, что ваш код Axios создается до того, как csrf_token отображается на странице (с помощью шаблона django)?

Попробуйте что-то подобное на раннем этапе своего js-кода:

var my_token = '{{ csrf_token }}';

затем, когда вы вызываете Axios, отправьте это значение вместо рендеринга {{csrf_token}} встроенным, что-то вроде этого:

{
    let payload = {
        "csrfmiddlewaretoken": my_token,
        "math_problem": problem,
        "user_answer": userInput,
        "true_answer": correctAnswer,
        "question_status": questionStatus,
    }
    console.log(payload);
    axios.post('../api/scoring/submit_score_details', payload)
}

Мне также любопытно, видели ли вы, что ваш токен csrf печатается на консоли в показанном вами коде (из распечатки полезной нагрузки), поскольку это укажет, отправляется он или нет.

Если был отправлен csrf_token, возможно, с приложением возникла какая-то другая проблема, но как только у вас есть токен в my_token, вы можете распечатать его и убедиться, что он настроен соответствующим образом, и работать оттуда.

person GoGoWorx    schedule 09.12.2020
comment
Я пробовал ваш метод, но без кубиков. Я получаю ту же ошибку. Я не думаю, что JS знает, что такое {{csrf_token}}. Токен не распечатывается в консоли. Вот как выглядит пример полезной нагрузки: csrfmiddlewaretoken: {{csrf_token}} math_problem: 4 x 1 question_status: правильно true_answer: 4 user_answer: 4 - person Hiebs915; 10.12.2020
comment
Используете ли вы Django для рендеринга вывода (который переводит {{csrf_token}} в требуемую строку? Вот почему я предложил загрузить токен в его собственную переменную, чтобы его можно было перепроверить. Если вы не использовать django для рендеринга html, тогда как вы его настраиваете? - person GoGoWorx; 10.12.2020
comment
Да, HTML отображается с помощью Django. Мой JS также находится в моем проекте Django, в моей статической папке. - person Hiebs915; 10.12.2020
comment
Хм, странно, что тогда он не конвертирует {{csrf_token}} для вас. Внутри формы он использует {% csrf_token%} для инициализации значения (и делает его доступным для JS), но возможно ли, что ваш код сгенерирован до того, как форма будет отрисована - вы упомянули это в статическом коде - возможно, переместите Axios вызовите свою форму (временно) после {% csrf_token%}, чтобы увидеть, делает ли это ее более счастливой - я думаю, что столкнулся с аналогичной проблемой некоторое время назад, поэтому я предложил создать переменную на раннем этапе вашего JS. Вы также можете установить точку останова, чтобы проверить ее значение - person GoGoWorx; 10.12.2020
comment
что именно я бы вставил в свой HTML-код из своего JS-файла? JS POST использует множество переменных и других элементов, которые являются частью файла JS. - person Hiebs915; 10.12.2020
comment
Похоже, вы могли бы просто поместить вызов Axios (как показано выше, let payload ... axios post ...) в свой HTML. Очевидно, вам нужно будет поместить его в тег ‹script›, но таким образом вы сможете выполнить вызов axios и увидеть токен csrf, поскольку токен должен быть действительным в вашей форме. Если это помогает, я сам много раз использовал аналогичные методы для рендеринга токена, поэтому знаю, что это работает. Вы можете использовать фиктивные значения для параметров полезной нагрузки, если вам это нужно во время этого теста. - person GoGoWorx; 10.12.2020

Хорошо, оглядываясь назад на обсуждение, я думаю, что могу знать, что здесь происходит ...

Возможно, вы добавили {{csrf_token}} в свой JS (где-то хранится в статическом коде) - это не сработает, поскольку, как вы отметили, JS ничего не знает о шаблонах django.

Способ сделать это - изменить код JS для использования переменной JS (например, my_token).

{
    let payload = {
        "csrfmiddlewaretoken": my_token, // populated later within form
        "math_problem": problem,
        "user_answer": userInput,
        "true_answer": correctAnswer,
        "question_status": questionStatus,
    }
    console.log(payload);
    axios.post('../api/scoring/submit_score_details', payload)
}

Это значение my_token будет предоставлено позже (в вашей форме) перед вызовом Axios. Для этого добавьте это в свою форму:

<script>
    // This will be rendered by Django to get the actual string into my_token
    var my_token = "{{ csrf_token }}"; 
</script>

Затем, когда ваш вызов Axios сделан (нажав кнопку, таймер или что-то еще), эта глобальная переменная должна быть правильно установлена ​​и использована.

person GoGoWorx    schedule 09.12.2020
comment
Могу ли я поместить это до или после моего JS-скрипта в нижней части моего div? - person Hiebs915; 10.12.2020
comment
Вы можете поместить его прямо под {% csrf_token%}, чтобы прояснить, что происходит, если хотите. - person GoGoWorx; 10.12.2020
comment
Получил новую информацию! Я забыл удалить var my_token '{{csrf_token}} в верхней части JS. Я удалил его и теперь в консоли распечатана строка токена. - person Hiebs915; 10.12.2020
comment
Однако я получаю ту же ошибку. - person Hiebs915; 10.12.2020