Skip to content

Commit

Permalink
Handle decoding errors like json decode errors (#428)
Browse files Browse the repository at this point in the history
* Handle decoding errors like json decode errors

When receiving a post containing non utf8 data with a Application/Json
content-type, the decoding fails.  This should be treated as an
incorrect JSON payload and return a 400.

fixes #427

* Update changelog and AUTHORS
  • Loading branch information
lindycoder authored and sloria committed Oct 6, 2019
1 parent fb8ff11 commit 6347b4b
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 2 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Expand Up @@ -40,3 +40,4 @@ Contributors (chronological)
* Xiaoyu Lee <https://github.com/lee3164>
* Jonathan Angelo <https://github.com/jangelo>
* @zhenhua32 <https://github.com/zhenhua32>
* Martin Roy <https://github.com/lindycoder>
8 changes: 8 additions & 0 deletions CHANGELOG.rst
@@ -1,6 +1,14 @@
Changelog
---------

5.5.2 (unreleased)
******************

Bug fixes:

* Handle ``UnicodeDecodeError`` when parsing JSON payloads (:issue:`427`).
Thanks :user:`lindycoder` for the catch and patch.

5.5.1 (2019-09-15)
******************

Expand Down
9 changes: 8 additions & 1 deletion src/webargs/aiohttpparser.py
Expand Up @@ -102,6 +102,9 @@ async def parse_json(self, req: Request, name: str, field: Field) -> typing.Any:
return core.missing
else:
return self.handle_invalid_json_error(e, req)
except UnicodeDecodeError as e:
return self.handle_invalid_json_error(e, req)

self._cache["json"] = json_data
return core.get_value(json_data, name, field, allow_many_nested=True)

Expand Down Expand Up @@ -164,7 +167,11 @@ def handle_error(
)

def handle_invalid_json_error(
self, error: json.JSONDecodeError, req: Request, *args, **kwargs
self,
error: typing.Union[json.JSONDecodeError, UnicodeDecodeError],
req: Request,
*args,
**kwargs
) -> "typing.NoReturn":
error_class = exception_map[400]
messages = {"json": ["Invalid JSON body."]}
Expand Down
3 changes: 3 additions & 0 deletions src/webargs/bottleparser.py
Expand Up @@ -47,6 +47,9 @@ def parse_json(self, req, name, field):
return core.missing
else:
return self.handle_invalid_json_error(e, req)
except UnicodeDecodeError as e:
return self.handle_invalid_json_error(e, req)

if json_data is None:
return core.missing
return core.get_value(json_data, name, field, allow_many_nested=True)
Expand Down
9 changes: 8 additions & 1 deletion src/webargs/core.py
Expand Up @@ -112,7 +112,14 @@ def get_value(data, name, field, allow_many_nested=False):

def parse_json(s, encoding="utf-8"):
if isinstance(s, bytes):
s = s.decode(encoding)
try:
s = s.decode(encoding)
except UnicodeDecodeError as e:
raise json.JSONDecodeError(
"Bytes decoding error : {}".format(e.reason),
doc=str(e.object),
pos=e.start,
)
return json.loads(s)


Expand Down
12 changes: 12 additions & 0 deletions src/webargs/testing.py
Expand Up @@ -117,6 +117,18 @@ def test_parse_json_with_nonascii_chars(self, testapp):
text = u"øˆƒ£ºº∆ƒˆ∆"
assert testapp.post_json("/echo", {"name": text}).json == {"name": text}

# https://github.com/marshmallow-code/webargs/issues/427
def test_parse_json_with_nonutf8_chars(self, testapp):
res = testapp.post(
"/echo",
b"\xfe",
headers={"Accept": "application/json", "Content-Type": "application/json"},
expect_errors=True,
)

assert res.status_code == 400
assert res.json == {"json": ["Invalid JSON body."]}

def test_validation_error_returns_422_response(self, testapp):
res = testapp.post("/echo", {"name": "b"}, expect_errors=True)
assert res.status_code == 422
Expand Down
12 changes: 12 additions & 0 deletions tests/test_falconparser.py
Expand Up @@ -16,6 +16,18 @@ def test_parse_files(self, testapp):
def test_use_args_hook(self, testapp):
assert testapp.get("/echo_use_args_hook?name=Fred").json == {"name": "Fred"}

# https://github.com/marshmallow-code/webargs/issues/427
def test_parse_json_with_nonutf8_chars(self, testapp):
res = testapp.post(
"/echo",
b"\xfe",
headers={"Accept": "application/json", "Content-Type": "application/json"},
expect_errors=True,
)

assert res.status_code == 400
assert res.json["errors"] == {"json": ["Invalid JSON body."]}

# https://github.com/sloria/webargs/issues/329
def test_invalid_json(self, testapp):
res = testapp.post(
Expand Down

0 comments on commit 6347b4b

Please sign in to comment.