python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.14k stars 333 forks source link

Werkzeug 2.1.2 Did not attempt to load JSON data #454

Open jbmoorhouse opened 2 years ago

jbmoorhouse commented 2 years ago

Code

from flask import Flask
from flask_restx import Api, Resource
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(
    app,
    version="1.0",
    title="Foo",
    description="A simple foo",
)

ns = api.namespace("Foo", description="bar")

@ns.route("/")
class Foo(Resource):
    """Shows a list of all todos, and lets you POST to add new tasks"""

    get_parser = api.parser()
    get_parser.add_argument(
        "foo",
        type=bool,
        default=True,
    )

    @ns.doc("foo", parser=get_parser)
    def get(self):
        """List all tasks"""
        self.get_parser.parse_args()
        return "foo"

if __name__ == "__main__":
    app.run(debug=True)

Repro Steps (if applicable)

  1. Run the app
  2. Then try out the only endpoint with the foo query param set as true/false
  3. Broken!

Expected Behavior

I would expect this to return a just and empty list (i.e. not really do anything)

Actual Behavior

{
  "message": "Did not attempt to load JSON data because the request Content-Type was not 'application/json'."
}

Error Messages/Stack Trace

parse_args raises this error in reqparse.py

Environment

jbmoorhouse commented 2 years ago

Bit of nasty temporary workaround, but it works for now

from flask import Flask as Flask_, Request as Request_

class Request(Request_):
    def get_json(self, *args, **kwargs):
        kwargs.update(silent=True)
        return super().get_json(*args, **kwargs)

class Flask(Flask_):
    request_class = Request

Then create your flask app using this new Flask object

peter-doggart commented 2 years ago

Think this is a duplicate of https://github.com/python-restx/flask-restx/issues/422 ?

shoeffner commented 2 years ago

Was about to comment, but then your comment showed up, @peter-doggart – indeed, it is a duplicate.

This also occured in https://stackoverflow.com/questions/72157708/flask-restx-request-parser-returns-400-bad-request.

The failing line is https://github.com/python-restx/flask-restx/blob/88497ced96674916403fa7829de693eaa3485a08/flask_restx/reqparse.py#L149 And the reason is that werkzeug raises if the content type does not match since 2.1: https://github.com/pallets/werkzeug/blob/acb1b04ef2e0ff269dc5313462c341dfaacb1b5b/src/werkzeug/wrappers/request.py#L537-L538

pype-leila commented 2 years ago

Is there any update on a fix for this issue?

Ryu-CZ commented 2 years ago

I fixed flask-restx/flask_restx/reqparse.py in my restx-monkey patches. I hope it will help you, feedback is welcome. :)

Golyo88 commented 3 weeks ago

This middleware adds Content-Type: application/json header to the POST, PUT, PATCH requests if there is not Content-Type header set in the request

class JSONContentTypeMiddleware:
    def __init__(self, wsgi_app: Any):
        self.wsgi_app = wsgi_app

    def __call__(self, environ: dict[str, Any], start_response: Any) -> Any:
        if environ["REQUEST_METHOD"] in ("POST", "PUT", "PATCH"):
            content_type = environ.get("CONTENT_TYPE")
            if not content_type:
                environ["CONTENT_TYPE"] = "application/json"

        return self.wsgi_app(environ, start_response)

And then

flask_app = Flask(__name__)
flask_app.wsgi_app = JSONContentTypeMiddleware(flask_app.wsgi_app)