Skip to content

Commit

Permalink
Handle Werkzeug 2.1.0 change to Request.get_json().
Browse files Browse the repository at this point in the history
pallets/werkzeug#2339 changed the behavior of `Request.get_json()`
and the `Request.json` property to raise a `BadRequest` if `Request.get_json()`
is called without `silent=True`, or the `Request.json` property is accessed,
and the content type is not `"application/json"`.

Argument parsing allows parsing from multiple locations, and defaults to
`["json", "values"]`, but if the locations include `"json"` and the content
type is not `"application/json"`, a `BadRequest` is now raised with Werkzeug >= 2.1.0.

Invoking `Request.get_json()` with the `silent=True` parameter now handles
the situation where `"json"` is included in the locations, but the content type
is not `"application/json"`.
  • Loading branch information
Stacy W. Smith authored and stacywsmith committed Apr 1, 2022
1 parent 88497ce commit d88f81d
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 9 deletions.
10 changes: 8 additions & 2 deletions flask_restx/reqparse.py
Expand Up @@ -138,15 +138,21 @@ def source(self, request):
:param request: The flask request object to parse arguments from
"""
if isinstance(self.location, six.string_types):
value = getattr(request, self.location, MultiDict())
if self.location in {"json", "get_json"}:
value = request.get_json(silent=True)
else:
value = getattr(request, self.location, MultiDict())
if callable(value):
value = value()
if value is not None:
return value
else:
values = MultiDict()
for l in self.location:
value = getattr(request, l, None)
if l in {"json", "get_json"}:
value = request.get_json(silent=True)
else:
value = getattr(request, l, None)
if callable(value):
value = value()
if value is not None:
Expand Down
16 changes: 9 additions & 7 deletions tests/test_reqparse.py
Expand Up @@ -41,8 +41,9 @@ def test_help(self, app, mocker):
)
parser = RequestParser()
parser.add_argument("foo", choices=("one", "two"), help="Bad choice.")
req = mocker.Mock(["values"])
req = mocker.Mock(["values", "get_json"])
req.values = MultiDict([("foo", "three")])
req.get_json.return_value = None
with pytest.raises(BadRequest):
parser.parse_args(req)
expected = {
Expand All @@ -58,7 +59,8 @@ def test_no_help(self, app, mocker):
)
parser = RequestParser()
parser.add_argument("foo", choices=["one", "two"])
req = mocker.Mock(["values"])
req = mocker.Mock(["values", "get_json"])
req.get_json.return_value = None
req.values = MultiDict([("foo", "three")])
with pytest.raises(BadRequest):
parser.parse_args(req)
Expand All @@ -76,9 +78,9 @@ def test_viewargs(self, mocker):
args = parser.parse_args(req)
assert args["foo"] == "bar"

req = mocker.Mock()
req = mocker.Mock(["get_json"])
req.values = ()
req.json = None
req.get_json.return_value = None
req.view_args = {"foo": "bar"}
parser = RequestParser()
parser.add_argument("foo", store_missing=True)
Expand All @@ -101,11 +103,10 @@ def test_parse_unicode_app(self, app):
args = parser.parse_args()
assert args["foo"] == "barß"

@pytest.mark.request_context("/bubble", method="post")
@pytest.mark.request_context("/bubble", method="post", content_type='application/json')
def test_json_location(self):
parser = RequestParser()
parser.add_argument("foo", location="json", store_missing=True)

args = parser.parse_args()
assert args["foo"] is None

Expand Down Expand Up @@ -856,7 +857,8 @@ def test_source_bad_location(self, mocker):
assert len(arg.source(req)) == 0 # yes, basically you don't find it

def test_source_default_location(self, mocker):
req = mocker.Mock(["values"])
req = mocker.Mock(["values", "get_json"])
req.get_json.return_value = None
req._get_child_mock = lambda **kwargs: MultiDict()
arg = Argument("foo")
assert arg.source(req) == req.values
Expand Down

0 comments on commit d88f81d

Please sign in to comment.