diff --git a/openapi_core/deserializing/media_types/factories.py b/openapi_core/deserializing/media_types/factories.py index b174bef..f44a5c0 100644 --- a/openapi_core/deserializing/media_types/factories.py +++ b/openapi_core/deserializing/media_types/factories.py @@ -1,4 +1,6 @@ -from openapi_core.deserializing.media_types.util import json_loads, form_loads +from openapi_core.deserializing.media_types.util import ( + json_loads, urlencoded_form_loads, data_form_loads, +) from openapi_core.deserializing.media_types.deserializers import ( PrimitiveDeserializer, @@ -9,7 +11,8 @@ class MediaTypeDeserializersFactory(object): MEDIA_TYPE_DESERIALIZERS = { 'application/json': json_loads, - 'application/x-www-form-urlencoded': form_loads, + 'application/x-www-form-urlencoded': urlencoded_form_loads, + 'multipart/form-data': data_form_loads, } def __init__(self, custom_deserializers=None): diff --git a/openapi_core/deserializing/media_types/util.py b/openapi_core/deserializing/media_types/util.py index bc5a88f..f1e6762 100644 --- a/openapi_core/deserializing/media_types/util.py +++ b/openapi_core/deserializing/media_types/util.py @@ -1,3 +1,4 @@ +from email.parser import Parser from json import loads from six import binary_type @@ -11,5 +12,19 @@ def json_loads(value): return loads(value) -def form_loads(value): +def urlencoded_form_loads(value): return dict(parse_qsl(value)) + + +def data_form_loads(value): + if issubclass(type(value), binary_type): + value = value.decode('ASCII', errors='surrogateescape') + parser = Parser() + parts = parser.parsestr(value, headersonly=False) + return dict( + ( + part.get_param('name', header='content-disposition'), + part.get_payload(decode=True), + ) + for part in parts.get_payload() + ) diff --git a/tests/unit/deserializing/test_media_types_deserializers.py b/tests/unit/deserializing/test_media_types_deserializers.py index 435feb4..40ea100 100644 --- a/tests/unit/deserializing/test_media_types_deserializers.py +++ b/tests/unit/deserializing/test_media_types_deserializers.py @@ -1,5 +1,7 @@ import pytest +from six import b, u + from openapi_core.deserializing.exceptions import DeserializeError from openapi_core.deserializing.media_types.factories import ( MediaTypeDeserializersFactory, @@ -31,7 +33,7 @@ class TestMediaTypeDeserializer(object): assert result == {} - def test_form_urlencoded_empty(self, deserializer_factory): + def test_urlencoded_form_empty(self, deserializer_factory): media_type = MediaType('application/x-www-form-urlencoded') value = '' @@ -39,7 +41,7 @@ class TestMediaTypeDeserializer(object): assert result == {} - def test_form_urlencoded_simple(self, deserializer_factory): + def test_urlencoded_form_simple(self, deserializer_factory): media_type = MediaType('application/x-www-form-urlencoded') value = 'param1=test' @@ -47,6 +49,30 @@ class TestMediaTypeDeserializer(object): assert result == {'param1': 'test'} + @pytest.mark.parametrize('value', [b(''), u('')]) + def test_data_form_empty(self, deserializer_factory, value): + media_type = MediaType('multipart/form-data') + + result = deserializer_factory(media_type)(value) + + assert result == {} + + def test_data_form_simple(self, deserializer_factory): + media_type = MediaType('multipart/form-data') + value = b( + 'Content-Type: multipart/form-data; boundary="' + '===============2872712225071193122=="\n' + 'MIME-Version: 1.0\n\n' + '--===============2872712225071193122==\n' + 'Content-Type: text/plain\nMIME-Version: 1.0\n' + 'Content-Disposition: form-data; name="param1"\n\ntest\n' + '--===============2872712225071193122==--\n' + ) + + result = deserializer_factory(media_type)(value) + + assert result == {'param1': b('test')} + def test_custom_simple(self, deserializer_factory): custom_mimetype = 'application/custom' media_type = MediaType(custom_mimetype)