mirror of
https://github.com/django/django.git
synced 2025-06-12 15:09:12 +00:00
Added __init__ to BaseParsers and subclasses.
This commit is contained in:
parent
e9a18b4a2a
commit
95d94515b6
@ -113,9 +113,9 @@ class ASGIRequest(HttpRequest):
|
|||||||
# Other bits.
|
# Other bits.
|
||||||
self.resolver_match = None
|
self.resolver_match = None
|
||||||
self._parsers = [
|
self._parsers = [
|
||||||
parsers.FormParser(),
|
parsers.FormParser,
|
||||||
parsers.MultiPartParser(),
|
parsers.MultiPartParser,
|
||||||
parsers.JSONParser(),
|
parsers.JSONParser,
|
||||||
]
|
]
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -79,9 +79,9 @@ class WSGIRequest(HttpRequest):
|
|||||||
self._read_started = False
|
self._read_started = False
|
||||||
self.resolver_match = None
|
self.resolver_match = None
|
||||||
self._parsers = [
|
self._parsers = [
|
||||||
parsers.FormParser(),
|
parsers.FormParser,
|
||||||
parsers.MultiPartParser(),
|
parsers.MultiPartParser,
|
||||||
parsers.JSONParser(),
|
parsers.JSONParser,
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_scheme(self):
|
def _get_scheme(self):
|
||||||
|
@ -10,39 +10,50 @@ class BaseParser:
|
|||||||
media_type = None
|
media_type = None
|
||||||
parsers = None
|
parsers = None
|
||||||
|
|
||||||
def can_handle(self, media_type):
|
def __init__(self, request):
|
||||||
return media_type == self.media_type
|
self.request = request
|
||||||
|
|
||||||
def parse(self, data, request=None):
|
@classmethod
|
||||||
|
def can_handle(cls, media_type):
|
||||||
|
return media_type == cls.media_type
|
||||||
|
|
||||||
|
def parse(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FormParser(BaseParser):
|
class FormParser(BaseParser):
|
||||||
media_type = "application/x-www-form-urlencoded"
|
media_type = "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
def parse(self, request):
|
def __init__(self, request):
|
||||||
from django.http import QueryDict
|
super().__init__(request)
|
||||||
|
|
||||||
# According to RFC 1866, the "application/x-www-form-urlencoded"
|
# According to RFC 1866, the "application/x-www-form-urlencoded"
|
||||||
# content type does not have a charset and should be always treated
|
# content type does not have a charset and should be always treated
|
||||||
# as UTF-8.
|
# as UTF-8.
|
||||||
if request._encoding is not None and request._encoding.lower() != "utf-8":
|
if (
|
||||||
|
self.request._encoding is not None
|
||||||
|
and self.request._encoding.lower() != "utf-8"
|
||||||
|
):
|
||||||
raise BadRequest(
|
raise BadRequest(
|
||||||
"HTTP requests with the 'application/x-www-form-urlencoded' "
|
"HTTP requests with the 'application/x-www-form-urlencoded' "
|
||||||
"content type must be UTF-8 encoded."
|
"content type must be UTF-8 encoded."
|
||||||
)
|
)
|
||||||
return QueryDict(request.body, encoding="utf-8"), MultiValueDict()
|
|
||||||
|
def parse(self, data):
|
||||||
|
from django.http import QueryDict
|
||||||
|
|
||||||
|
return QueryDict(data, encoding="utf-8"), MultiValueDict()
|
||||||
|
|
||||||
|
|
||||||
class MultiPartParser(BaseParser):
|
class MultiPartParser(BaseParser):
|
||||||
media_type = "multipart/form-data"
|
media_type = "multipart/form-data"
|
||||||
|
|
||||||
def parse(self, request):
|
def parse(self, data):
|
||||||
|
request = self.request
|
||||||
if hasattr(request, "_body"):
|
if hasattr(request, "_body"):
|
||||||
# Use already read data
|
# Use already read data
|
||||||
data = BytesIO(request._body)
|
request_data = BytesIO(request._body)
|
||||||
else:
|
else:
|
||||||
data = request
|
request_data = request
|
||||||
|
|
||||||
# TODO - POST and data can be called on the same request. This parser can be
|
# TODO - POST and data can be called on the same request. This parser can be
|
||||||
# called multiple times on the same request. While `_post` `_data` are different
|
# called multiple times on the same request. While `_post` `_data` are different
|
||||||
@ -56,7 +67,11 @@ class MultiPartParser(BaseParser):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser = _MultiPartParser(
|
parser = _MultiPartParser(
|
||||||
request.META, data, request.upload_handlers, request.encoding, self.parsers
|
request.META,
|
||||||
|
request_data,
|
||||||
|
request.upload_handlers,
|
||||||
|
request.encoding,
|
||||||
|
self.parsers,
|
||||||
)
|
)
|
||||||
# TODO _post could also be _data
|
# TODO _post could also be _data
|
||||||
_post, _files = parser.parse()
|
_post, _files = parser.parse()
|
||||||
@ -66,15 +81,10 @@ class MultiPartParser(BaseParser):
|
|||||||
class JSONParser(BaseParser):
|
class JSONParser(BaseParser):
|
||||||
media_type = "application/json"
|
media_type = "application/json"
|
||||||
|
|
||||||
# TODO rename request -- it's not always one.
|
def parse(self, data):
|
||||||
def parse(self, request):
|
|
||||||
from django.http import HttpRequest
|
|
||||||
|
|
||||||
def strict_constant(o):
|
def strict_constant(o):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Out of range float values are not JSON compliant: " + repr(o)
|
"Out of range float values are not JSON compliant: " + repr(o)
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(request, HttpRequest):
|
return json.loads(data, parse_constant=strict_constant), MultiValueDict()
|
||||||
request = request.body
|
|
||||||
return json.loads(request, parse_constant=strict_constant), MultiValueDict()
|
|
||||||
|
@ -70,9 +70,9 @@ class HttpRequest:
|
|||||||
self.content_type = None
|
self.content_type = None
|
||||||
self.content_params = None
|
self.content_params = None
|
||||||
self._parsers = [
|
self._parsers = [
|
||||||
parsers.FormParser(),
|
parsers.FormParser,
|
||||||
parsers.MultiPartParser(),
|
parsers.MultiPartParser,
|
||||||
parsers.JSONParser(),
|
parsers.JSONParser,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -366,7 +366,7 @@ class HttpRequest:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if parser_list is None:
|
if parser_list is None:
|
||||||
parser_list = [parsers.FormParser(), parsers.MultiPartParser()]
|
parser_list = [parsers.FormParser, parsers.MultiPartParser]
|
||||||
selected_parser = None
|
selected_parser = None
|
||||||
for parser in parser_list:
|
for parser in parser_list:
|
||||||
if parser.can_handle(self.content_type):
|
if parser.can_handle(self.content_type):
|
||||||
@ -374,9 +374,13 @@ class HttpRequest:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if selected_parser:
|
if selected_parser:
|
||||||
selected_parser.parsers = parser_list
|
parser = selected_parser(self)
|
||||||
try:
|
try:
|
||||||
data, self._files = parser.parse(self)
|
if self.content_type == "multipart/form-data":
|
||||||
|
parser.parsers = (parser(self) for parser in parser_list)
|
||||||
|
data, self._files = parser.parse(None)
|
||||||
|
else:
|
||||||
|
data, self._files = parser.parse(self.body)
|
||||||
setattr(self, data_attr, data)
|
setattr(self, data_attr, data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO 'application/x-www-form-urlencoded' didn't do this.
|
# TODO 'application/x-www-form-urlencoded' didn't do this.
|
||||||
|
@ -8,11 +8,11 @@ from django.utils.http import urlencode
|
|||||||
|
|
||||||
class TestParsers(SimpleTestCase):
|
class TestParsers(SimpleTestCase):
|
||||||
def test_can_handle(self):
|
def test_can_handle(self):
|
||||||
parser = MultiPartParser()
|
parser = MultiPartParser(HttpRequest())
|
||||||
self.assertIs(parser.can_handle("multipart/form-data"), True)
|
self.assertIs(parser.can_handle("multipart/form-data"), True)
|
||||||
self.assertIs(parser.can_handle("application/json"), False)
|
self.assertIs(parser.can_handle("application/json"), False)
|
||||||
|
|
||||||
parser = FormParser()
|
parser = FormParser(HttpRequest())
|
||||||
self.assertIs(parser.can_handle("application/x-www-form-urlencoded"), True)
|
self.assertIs(parser.can_handle("application/x-www-form-urlencoded"), True)
|
||||||
self.assertIs(parser.can_handle("multipart/form-data"), False)
|
self.assertIs(parser.can_handle("multipart/form-data"), False)
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ class TestParsers(SimpleTestCase):
|
|||||||
main_type, sub_type = media_type.split("/")
|
main_type, sub_type = media_type.split("/")
|
||||||
return main_type == "text"
|
return main_type == "text"
|
||||||
|
|
||||||
parser = CustomParser()
|
parser = CustomParser(None)
|
||||||
self.assertIs(parser.can_handle("application/json"), False)
|
self.assertIs(parser.can_handle("application/json"), False)
|
||||||
self.assertTrue(parser.can_handle("text/*"), True)
|
self.assertTrue(parser.can_handle("text/*"), True)
|
||||||
self.assertTrue(parser.can_handle("text/csv"), True)
|
self.assertTrue(parser.can_handle("text/csv"), True)
|
||||||
@ -32,16 +32,16 @@ class TestParsers(SimpleTestCase):
|
|||||||
def test_request_parser_no_setting(self):
|
def test_request_parser_no_setting(self):
|
||||||
request = HttpRequest()
|
request = HttpRequest()
|
||||||
form, multipart, json = request.parsers
|
form, multipart, json = request.parsers
|
||||||
self.assertIsInstance(form, FormParser)
|
self.assertIs(form, FormParser)
|
||||||
self.assertIsInstance(multipart, MultiPartParser)
|
self.assertIs(multipart, MultiPartParser)
|
||||||
self.assertIsInstance(json, JSONParser)
|
self.assertIs(json, JSONParser)
|
||||||
|
|
||||||
def test_set_parser(self):
|
def test_set_parser(self):
|
||||||
request = HttpRequest()
|
request = HttpRequest()
|
||||||
request.parsers = [FormParser()]
|
request.parsers = [FormParser]
|
||||||
|
|
||||||
self.assertEqual(len(request.parsers), 1)
|
self.assertEqual(len(request.parsers), 1)
|
||||||
self.assertIsInstance(request.parsers[0], FormParser)
|
self.assertIs(request.parsers[0], FormParser)
|
||||||
|
|
||||||
def test_set_parsers_following_files_access(self):
|
def test_set_parsers_following_files_access(self):
|
||||||
payload = FakePayload(urlencode({"key": "value"}))
|
payload = FakePayload(urlencode({"key": "value"}))
|
||||||
@ -62,7 +62,7 @@ class TestParsers(SimpleTestCase):
|
|||||||
request.parsers = []
|
request.parsers = []
|
||||||
|
|
||||||
def test_json_strict(self):
|
def test_json_strict(self):
|
||||||
parser = JSONParser()
|
parser = JSONParser(None)
|
||||||
|
|
||||||
msg_base = "Out of range float values are not JSON compliant: '%s'"
|
msg_base = "Out of range float values are not JSON compliant: '%s'"
|
||||||
for value in ["Infinity", "-Infinity", "NaN"]:
|
for value in ["Infinity", "-Infinity", "NaN"]:
|
||||||
|
@ -673,6 +673,34 @@ class RequestsTests(SimpleTestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_data_form_data_json(self):
|
||||||
|
payload = FakePayload(
|
||||||
|
"\r\n".join([f"--{BOUNDARY}", *self._json_payload, f"--{BOUNDARY}--"])
|
||||||
|
)
|
||||||
|
request = WSGIRequest(
|
||||||
|
{
|
||||||
|
"REQUEST_METHOD": "POST",
|
||||||
|
"CONTENT_TYPE": MULTIPART_CONTENT,
|
||||||
|
"CONTENT_LENGTH": len(payload),
|
||||||
|
"wsgi.input": payload,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request.data,
|
||||||
|
{
|
||||||
|
"JSON": [
|
||||||
|
{
|
||||||
|
"pk": 1,
|
||||||
|
"model": "store.book",
|
||||||
|
"fields": {
|
||||||
|
"name": "Mostly Harmless",
|
||||||
|
"author": ["Douglas", "Adams"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_POST_multipart_json(self):
|
def test_POST_multipart_json(self):
|
||||||
payload = FakePayload(
|
payload = FakePayload(
|
||||||
"\r\n".join(
|
"\r\n".join(
|
||||||
@ -1036,6 +1064,7 @@ class RequestsTests(SimpleTestCase):
|
|||||||
)
|
)
|
||||||
request.body # evaluate
|
request.body # evaluate
|
||||||
self.assertEqual(request.POST, {"name": ["value"]})
|
self.assertEqual(request.POST, {"name": ["value"]})
|
||||||
|
self.assertEqual(request.data, {"name": ["value"]})
|
||||||
|
|
||||||
def test_multipart_post_field_with_invalid_base64(self):
|
def test_multipart_post_field_with_invalid_base64(self):
|
||||||
payload = FakePayload(
|
payload = FakePayload(
|
||||||
@ -1061,6 +1090,7 @@ class RequestsTests(SimpleTestCase):
|
|||||||
)
|
)
|
||||||
request.body # evaluate
|
request.body # evaluate
|
||||||
self.assertEqual(request.POST, {"name": ["123"]})
|
self.assertEqual(request.POST, {"name": ["123"]})
|
||||||
|
self.assertEqual(request.data, {"name": ["123"]})
|
||||||
|
|
||||||
def test_POST_after_body_read_and_stream_read_multipart(self):
|
def test_POST_after_body_read_and_stream_read_multipart(self):
|
||||||
"""
|
"""
|
||||||
@ -1090,6 +1120,7 @@ class RequestsTests(SimpleTestCase):
|
|||||||
# Consume enough data to mess up the parsing:
|
# Consume enough data to mess up the parsing:
|
||||||
self.assertEqual(request.read(13), b"--boundary\r\nC")
|
self.assertEqual(request.read(13), b"--boundary\r\nC")
|
||||||
self.assertEqual(request.POST, {"name": ["value"]})
|
self.assertEqual(request.POST, {"name": ["value"]})
|
||||||
|
self.assertEqual(request.data, {"name": ["value"]})
|
||||||
|
|
||||||
def test_POST_immutable_for_multipart(self):
|
def test_POST_immutable_for_multipart(self):
|
||||||
"""
|
"""
|
||||||
@ -1115,6 +1146,7 @@ class RequestsTests(SimpleTestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertFalse(request.POST._mutable)
|
self.assertFalse(request.POST._mutable)
|
||||||
|
self.assertFalse(request.data._mutable)
|
||||||
|
|
||||||
def test_multipart_without_boundary(self):
|
def test_multipart_without_boundary(self):
|
||||||
request = WSGIRequest(
|
request = WSGIRequest(
|
||||||
@ -1129,6 +1161,10 @@ class RequestsTests(SimpleTestCase):
|
|||||||
MultiPartParserError, "Invalid boundary in multipart: None"
|
MultiPartParserError, "Invalid boundary in multipart: None"
|
||||||
):
|
):
|
||||||
request.POST
|
request.POST
|
||||||
|
with self.assertRaisesMessage(
|
||||||
|
MultiPartParserError, "Invalid boundary in multipart: None"
|
||||||
|
):
|
||||||
|
request.data
|
||||||
|
|
||||||
def test_multipart_non_ascii_content_type(self):
|
def test_multipart_non_ascii_content_type(self):
|
||||||
request = WSGIRequest(
|
request = WSGIRequest(
|
||||||
@ -1145,6 +1181,8 @@ class RequestsTests(SimpleTestCase):
|
|||||||
)
|
)
|
||||||
with self.assertRaisesMessage(MultiPartParserError, msg):
|
with self.assertRaisesMessage(MultiPartParserError, msg):
|
||||||
request.POST
|
request.POST
|
||||||
|
with self.assertRaisesMessage(MultiPartParserError, msg):
|
||||||
|
request.data
|
||||||
|
|
||||||
def test_multipart_with_header_fields_too_large(self):
|
def test_multipart_with_header_fields_too_large(self):
|
||||||
payload = FakePayload(
|
payload = FakePayload(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user