diff --git a/src/graceful/resources/base.py b/src/graceful/resources/base.py index d9aa9df..18c8e3d 100644 --- a/src/graceful/resources/base.py +++ b/src/graceful/resources/base.py @@ -5,6 +5,7 @@ from falcon import errors import falcon +from mimeparse import parse_mime_type from graceful.parameters import BaseParam, IntParam from graceful.errors import DeserializationError, ValidationError @@ -297,13 +298,22 @@ def require_representation(self, req): dict: raw dictionary of representation supplied in request body """ + try: + type_, subtype, _ = parse_mime_type(req.content_type) + content_type = '/'.join((type_, subtype)) + except: + raise falcon.HTTPUnsupportedMediaType( + description="Invalid Content-Type header: {}".format( + req.content_type + ) + ) - if req.content_type == 'application/json': + if content_type == 'application/json': body = req.stream.read() return json.loads(body.decode('utf-8')) else: raise falcon.HTTPUnsupportedMediaType( - description="only JSON supported" + description="only JSON supported, got: {}".format(content_type) ) def require_validated(self, req, partial=False): diff --git a/tests/test_resources.py b/tests/test_resources.py index a27782e..ed79f43 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -300,3 +300,47 @@ class TestResource(Resource): with pytest.raises(errors.HTTPBadRequest): resource.require_validated(Request(env)) + + +def test_require_representation_application_json(): + resource = TestResource() + + # simple application/json content type + env = create_environ( + body=json.dumps({'one': 'foo', 'two': 'foo'}), + headers={'Content-Type': 'application/json'}, + ) + + representation = resource.require_representation(Request(env)) + assert isinstance(representation, dict) + + # application/json content type with charset param + env = create_environ( + body=json.dumps({'one': 'foo', 'two': 'foo'}), + headers={'Content-Type': 'application/json; charset=UTF-8'}, + ) + + representation = resource.require_representation(Request(env)) + assert isinstance(representation, dict) + + +def test_require_representation_unsupported_media_type(): + resource = TestResource() + + # invalid content type format + env = create_environ( + body=json.dumps({'one': 'foo', 'two': 'foo'}), + headers={'Content-Type': 'foo bar'}, + ) + + with pytest.raises(falcon.HTTPUnsupportedMediaType): + resource.require_representation(Request(env)) + + # valid format but surely unsupported (RFC-1437) + env = create_environ( + body=json.dumps({'one': 'foo', 'two': 'foo'}), + headers={'Content-Type': 'matter-transport/sentient-life-form'}, + ) + + with pytest.raises(falcon.HTTPUnsupportedMediaType): + resource.require_representation(Request(env))