22

I am writing a small flask based site and I would like to send data from the client to the server using Ajax. Until now I have only used Ajax requests to retrieve data from the server. This time I would like to submit data via POST request.

This is the receiver on the flask side, I reduced it to barely log a message to avoid any unnecessary errors within the implementation of this route:

@app.route("/json_submit", methods=["POST"])
def submit_handler():
    # a = request.get_json(force=True)
    app.logger.log("json_submit")
    return {}

When submitting the ajax request, flask gives me a 400 error

127.0.0.1 - - [03/Apr/2014 09:18:50] "POST /json_submit HTTP/1.1" 400 -

I can also see this in the web developer console in the browser

Why is flask not calling submit_handler with the supplied data in the request?

 var request = $.ajax({
    url: "/json_submit",
    type: "POST",
    data: {
      id: id, 
      known: is_known
    },  
    dataType: "json",
  })  
   .done( function (request) {
  })
0

3 Answers 3

33

If you are using the Flask-WTF CSRF protection you'll need to either exempt your view or include the CSRF token in your AJAX POST request too.

Exempting is done with a decorator:

@csrf.exempt
@app.route("/json_submit", methods=["POST"])
def submit_handler():
    # a = request.get_json(force=True)
    app.logger.log("json_submit")
    return {}

To include the token with AJAX requests, interpolate the token into the page somewhere; in a <meta> header or in generated JavaScript, then set a X-CSRFToken header. When using jQuery, use the ajaxSetup hook.

Example using a meta tag (from the Flask-WTF CSRF documentation):

<meta name="csrf-token" content="{{ csrf_token() }}">

and in your JS code somewhere:

var csrftoken = $('meta[name=csrf-token]').attr('content')

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken)
        }
    }
})

Your handler doesn't actually post JSON data yet; it is still a regular url-encoded POST (the data will end up in request.form on the Flask side); you'd have to set the AJAX content type to application/json and use JSON.stringify() to actually submit JSON:

var request = $.ajax({
   url: "/json_submit",
   type: "POST",
   contentType: "application/json",
   data: JSON.stringify({
     id: id, 
     known: is_known
   }),  
})  
  .done( function (request) {
})

and now the data can be accessed as a Python structure with the request.get_json() method.

The dataType: "json", parameter to $.ajax is only needed when your view returns JSON (e.g. you used flask.json.jsonify() to produce a JSON response). It lets jQuery know how to process the response.

Sign up to request clarification or add additional context in comments.

6 Comments

Holy moly, thank you for the first sentence in this answer. I had exempt one view (the main form) with @csrf.exempt but had forgotten to add that same decorator to my actual upload view, which was being called via AJAX. Reading that first sentence somehow set off the lightbulb :)
Hello, you actually do not need to set csrf to a meta tag. Just set the variable like this in your javascript : var csrftoken = "{{ csrf_token() }}"; And by the way do not exempt the token of your route! Otherwise csrf protection is useless... Hope it helps. If needed I can edit an answer.
@pierrelb: I generally use static files for JS code (so it can be cached in the browser long term), and then using a meta tag or data attribute in the dynamic page is a far more scalable option. The answer does include the phrase or in generated JavaScript. And I also clearly state that exempting is an alternative to setting the token in a X-CSRFToken HTTP header.
@pierrelb: in other words: there is no need to edit, those options are already covered.
@deesolie: CSRF protection doesn't apply to GET, HEAD, OPTIONS or TRACE requests, so that test avoids adding the header to those requests. See it as a bandwidth-saving measure. You could invert the test, and match your Flask-WTF configuration WTF_CSRF_METHODS option.
|
2

Can you try like this

var request = $.ajax({
    url: "/json_submit",
    type: "POST",
    contentType: "application/json",
    data: JSON.stringify({
      id: id, 
      known: is_known
    }),  
    dataType: "json",
  })  
   .done( function (request) {
 })

Before that, In your code returns dict object. That is not correct. It returns json like

@app.route("/json_submit", methods=["POST"])
def submit_handler():
    # a = request.get_json(force=True)
    app.logger.log("json_submit")
    return flask.jsonify({'msg': 'success'})

Comments

0

A similar solution that does not require jQuery

<meta name="csrf-token" content="{{ csrf_token() }}">

var beforeSend = function(xhr) {
    var csrf_token = document.querySelector('meta[name=csrf-token]').content;
    xhr.setRequestHeader("X-CSRFToken", csrf_token);
};

function fooFunction() {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/json-submit");
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            // Do what you want with this.responseText
        }
    };
    beforeSend(xhr);
    xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xhr.send(JSON.stringify({
        'id': id, 'known': is_known
    }));
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.