31

I have a flask app with the following view:

@menus.route('/', methods=["PUT", "POST"])
def new():
    return jsonify(request.json)

However, this only works if the request's content type is set to application/json, otherwise the dict request.json is None.

I know that request.data has the request body as a string, but I don't want to be parsing it to a dict everytime a client forgets to set the request's content-type.

Is there a way to assume that every incoming request's content-type is application/json? All I want is to always have access to a valid request.json dict, even if the client forgets to set the application content-type to json.

danielrvt
  • 10,177
  • 20
  • 80
  • 121
  • Am I reading this correctly that you just want to return request data in a response? How can you make sure you get JSON then? – favoretti Jan 01 '13 at 17:14

2 Answers2

58

Use request.get_json() and set force to True:

@menus.route('/', methods=["PUT", "POST"])
def new():
    return jsonify(request.get_json(force=True))

From the documentation:

By default this function will only load the json data if the mimetype is application/json but this can be overridden by the force parameter.

Parameters:

  • force – if set to True the mimetype is ignored.

For older Flask versions, < 0.10, if you want to be forgiving and allow for JSON, always, you can do the decode yourself, explicitly:

from flask import json

@menus.route('/', methods=["PUT", "POST"])
def new():
    return jsonify(json.loads(request.data))
Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thats what I ended up doing :) – danielrvt Jan 01 '13 at 17:39
  • @RonReiter: Why doesn't it? We take the `request.data` **regardless** of the content type specified by the client. Sure, if the client sent invalid request data (e.g. not (valid) JSON), nothing can be done. – Martijn Pieters Jul 14 '13 at 14:04
  • 6
    @MartijnPieters request.data is an empty string when using www/form-url-encoded because it parses it as a form. From the documentation: data - Contains the incoming request data as string in case it came with a mimetype Flask does not handle. – Ron Reiter Jul 14 '13 at 14:12
  • Right, in narrow cases, where a client completely mislabeled the request, the above will fail. How likely is that case, and isn't that true for many other cases where a client malformed the request? – Martijn Pieters Jul 14 '13 at 15:17
  • Is it a bad idea or not recommended to make any coming data to json? I mean always use `request.get_json(force=True)` any where in back-end to extract data. – Tony Chou Apr 12 '18 at 13:14
  • 2
    @TonyChou: gating on the Content-Type header is one way of blocking bad clients early. If you use `force=True` just because some clients may have forgotten to set the header, then your server will have to do more work (because now all requests could contain JSON and time is spent trying to decode it all). – Martijn Pieters Apr 12 '18 at 18:42
  • This is actually a way to protect your app against CSRF attacks. If someone makes a POST to your app from another site, and your app uses auth cookies that are not `SameSite`, then these cookies will be automatically attached to the request, and without `Content-Type: application/json` header present, the request would be considered "simple" and thus submitted to the server. So this ends up making your app vulnerable to CSRF, unless you check Content-Type (this happens irregardless of if/how you set CORS headers btw, the POST request would still be submitted for a "simple" request). – dan Apr 08 '21 at 23:23
13

the request object already has a method get_json which can give you the json regardless of the content-type if you execute it with force=True so your code would be something like the following:

@menus.route('/', methods=["PUT", "POST"])
def new():
    return jsonify(request.get_json(force=True))

in fact, the flask documentation says that request.get_json should be used instead of request.json: http://flask.pocoo.org/docs/api/?highlight=json#flask.Request.json

josebama
  • 848
  • 8
  • 11