0
votes

I'm doing a POST request using Axios in my JS code to send some information to my locally hosted Django server. I have {% csrf_token %} in my html code form but don't know how to send the csrf token using Axios.

I get this error in the terminal: "Forbidden (CSRF token missing or incorrect.): /api/scoring/submit_score_details".

How do I correctly insert the csrf token in my axios post? Right now, I don't think JS can read {{ csrf_token }} as I have it. I've been searching Stack but it seems most people are using jQuery or some other type of JS.

To save space I didn't post what the variables in the payload are but they're all just strings.

I can get the error to go away if I put @csrf_exempt above the my function in the views.py file.

{
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>
3

3 Answers

2
votes

So I ended up doing some googling and asking a friend. We came up with a solution.

We had to add two lines of code to make things work:

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

We also got rid of the 'payload' variable and just put everything in the Axios code.

{
        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>

Here's a link that helped us.

0
votes

Is is possible your Axios code is being created before the csrf_token is being rendered in the page (by the django template)?

Try something like this early on in your js code:

var my_token = '{{ csrf_token }}';

then when you call Axios, send this value, instead of rendering {{ csrf_token }} inline, so something like this:

{
    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)
}

I'm also curious as to whether you saw your csrf token being printed to the console in the code you showed (from printing out payload), since this would indicate whether it was being sent or not.

If the csrf_token was being sent, it is possible there may be some other issue going on with the app, but once you have the token in my_token, you can print it out and see that it's set appropriately and work from there.

0
votes

Ok, looking back at the discussion, I think I might know what's going on here...

Maybe you've added the "{{ csrf_token }}" inside your JS (stored in static code somewhere) -- this won't work, since as you pointed out, JS knows nothing about django templates.

The way to do this would be to change your JS code to use a JS variable (such as 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)
}

This my_token value will be supplied later (within your form) before the Axios call is made. To do this, add this in your form:

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

Then, when your Axios call is made (by clicking a button or on a timer or whatever), this global variable should be correctly set and used.