Flask and Ajax Post HTTP 400 Bad Request Error
Solution 1
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.
Solution 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'})
Related videos on Youtube
wirrbel
data scientist and coder, interested in machine learning, PLs and all ways of abstraction.
Updated on June 06, 2022Comments
-
wirrbel almost 2 years
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) { })
-
sofly almost 9 yearsHoly 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 actualupload
view, which was being called via AJAX. Reading that first sentence somehow set off the lightbulb :) -
pierrelb about 7 yearsHello, 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.
-
Martijn Pieters about 7 years@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 aX-CSRFToken
HTTP header. -
Martijn Pieters about 7 years@pierrelb: in other words: there is no need to edit, those options are already covered.
-
deesolie over 3 yearsThank you a million times for this answer. Was banging my head against the wall for hours. The ajax set up with the CSRF token is clutch. Why do we need the
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)
? -
Martijn Pieters over 3 years@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.