mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +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.
|
||||
self.resolver_match = None
|
||||
self._parsers = [
|
||||
parsers.FormParser(),
|
||||
parsers.MultiPartParser(),
|
||||
parsers.JSONParser(),
|
||||
parsers.FormParser,
|
||||
parsers.MultiPartParser,
|
||||
parsers.JSONParser,
|
||||
]
|
||||
|
||||
@cached_property
|
||||
|
@ -79,9 +79,9 @@ class WSGIRequest(HttpRequest):
|
||||
self._read_started = False
|
||||
self.resolver_match = None
|
||||
self._parsers = [
|
||||
parsers.FormParser(),
|
||||
parsers.MultiPartParser(),
|
||||
parsers.JSONParser(),
|
||||
parsers.FormParser,
|
||||
parsers.MultiPartParser,
|
||||
parsers.JSONParser,
|
||||
]
|
||||
|
||||
def _get_scheme(self):
|
||||
|
@ -10,39 +10,50 @@ class BaseParser:
|
||||
media_type = None
|
||||
parsers = None
|
||||
|
||||
def can_handle(self, media_type):
|
||||
return media_type == self.media_type
|
||||
def __init__(self, request):
|
||||
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
|
||||
|
||||
|
||||
class FormParser(BaseParser):
|
||||
media_type = "application/x-www-form-urlencoded"
|
||||
|
||||
def parse(self, request):
|
||||
from django.http import QueryDict
|
||||
|
||||
def __init__(self, request):
|
||||
super().__init__(request)
|
||||
# According to RFC 1866, the "application/x-www-form-urlencoded"
|
||||
# content type does not have a charset and should be always treated
|
||||
# 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(
|
||||
"HTTP requests with the 'application/x-www-form-urlencoded' "
|
||||
"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):
|
||||
media_type = "multipart/form-data"
|
||||
|
||||
def parse(self, request):
|
||||
def parse(self, data):
|
||||
request = self.request
|
||||
if hasattr(request, "_body"):
|
||||
# Use already read data
|
||||
data = BytesIO(request._body)
|
||||
request_data = BytesIO(request._body)
|
||||
else:
|
||||
data = request
|
||||
request_data = request
|
||||
|
||||
# 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
|
||||
@ -56,7 +67,11 @@ class MultiPartParser(BaseParser):
|
||||
),
|
||||
)
|
||||
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
|
||||
_post, _files = parser.parse()
|
||||
@ -66,15 +81,10 @@ class MultiPartParser(BaseParser):
|
||||
class JSONParser(BaseParser):
|
||||
media_type = "application/json"
|
||||
|
||||
# TODO rename request -- it's not always one.
|
||||
def parse(self, request):
|
||||
from django.http import HttpRequest
|
||||
|
||||
def parse(self, data):
|
||||
def strict_constant(o):
|
||||
raise ValueError(
|
||||
"Out of range float values are not JSON compliant: " + repr(o)
|
||||
)
|
||||
|
||||
if isinstance(request, HttpRequest):
|
||||
request = request.body
|
||||
return json.loads(request, parse_constant=strict_constant), MultiValueDict()
|
||||
return json.loads(data, parse_constant=strict_constant), MultiValueDict()
|
||||
|
@ -70,9 +70,9 @@ class HttpRequest:
|
||||
self.content_type = None
|
||||
self.content_params = None
|
||||
self._parsers = [
|
||||
parsers.FormParser(),
|
||||
parsers.MultiPartParser(),
|
||||
parsers.JSONParser(),
|
||||
parsers.FormParser,
|
||||
parsers.MultiPartParser,
|
||||
parsers.JSONParser,
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
@ -366,7 +366,7 @@ class HttpRequest:
|
||||
return
|
||||
|
||||
if parser_list is None:
|
||||
parser_list = [parsers.FormParser(), parsers.MultiPartParser()]
|
||||
parser_list = [parsers.FormParser, parsers.MultiPartParser]
|
||||
selected_parser = None
|
||||
for parser in parser_list:
|
||||
if parser.can_handle(self.content_type):
|
||||
@ -374,9 +374,13 @@ class HttpRequest:
|
||||
break
|
||||
|
||||
if selected_parser:
|
||||
selected_parser.parsers = parser_list
|
||||
parser = selected_parser(self)
|
||||
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)
|
||||
except Exception as e:
|
||||
# TODO 'application/x-www-form-urlencoded' didn't do this.
|
||||
|
@ -8,11 +8,11 @@ from django.utils.http import urlencode
|
||||
|
||||
class TestParsers(SimpleTestCase):
|
||||
def test_can_handle(self):
|
||||
parser = MultiPartParser()
|
||||
parser = MultiPartParser(HttpRequest())
|
||||
self.assertIs(parser.can_handle("multipart/form-data"), True)
|
||||
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("multipart/form-data"), False)
|
||||
|
||||
@ -24,7 +24,7 @@ class TestParsers(SimpleTestCase):
|
||||
main_type, sub_type = media_type.split("/")
|
||||
return main_type == "text"
|
||||
|
||||
parser = CustomParser()
|
||||
parser = CustomParser(None)
|
||||
self.assertIs(parser.can_handle("application/json"), False)
|
||||
self.assertTrue(parser.can_handle("text/*"), True)
|
||||
self.assertTrue(parser.can_handle("text/csv"), True)
|
||||
@ -32,16 +32,16 @@ class TestParsers(SimpleTestCase):
|
||||
def test_request_parser_no_setting(self):
|
||||
request = HttpRequest()
|
||||
form, multipart, json = request.parsers
|
||||
self.assertIsInstance(form, FormParser)
|
||||
self.assertIsInstance(multipart, MultiPartParser)
|
||||
self.assertIsInstance(json, JSONParser)
|
||||
self.assertIs(form, FormParser)
|
||||
self.assertIs(multipart, MultiPartParser)
|
||||
self.assertIs(json, JSONParser)
|
||||
|
||||
def test_set_parser(self):
|
||||
request = HttpRequest()
|
||||
request.parsers = [FormParser()]
|
||||
request.parsers = [FormParser]
|
||||
|
||||
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):
|
||||
payload = FakePayload(urlencode({"key": "value"}))
|
||||
@ -62,7 +62,7 @@ class TestParsers(SimpleTestCase):
|
||||
request.parsers = []
|
||||
|
||||
def test_json_strict(self):
|
||||
parser = JSONParser()
|
||||
parser = JSONParser(None)
|
||||
|
||||
msg_base = "Out of range float values are not JSON compliant: '%s'"
|
||||
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):
|
||||
payload = FakePayload(
|
||||
"\r\n".join(
|
||||
@ -1036,6 +1064,7 @@ class RequestsTests(SimpleTestCase):
|
||||
)
|
||||
request.body # evaluate
|
||||
self.assertEqual(request.POST, {"name": ["value"]})
|
||||
self.assertEqual(request.data, {"name": ["value"]})
|
||||
|
||||
def test_multipart_post_field_with_invalid_base64(self):
|
||||
payload = FakePayload(
|
||||
@ -1061,6 +1090,7 @@ class RequestsTests(SimpleTestCase):
|
||||
)
|
||||
request.body # evaluate
|
||||
self.assertEqual(request.POST, {"name": ["123"]})
|
||||
self.assertEqual(request.data, {"name": ["123"]})
|
||||
|
||||
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:
|
||||
self.assertEqual(request.read(13), b"--boundary\r\nC")
|
||||
self.assertEqual(request.POST, {"name": ["value"]})
|
||||
self.assertEqual(request.data, {"name": ["value"]})
|
||||
|
||||
def test_POST_immutable_for_multipart(self):
|
||||
"""
|
||||
@ -1115,6 +1146,7 @@ class RequestsTests(SimpleTestCase):
|
||||
}
|
||||
)
|
||||
self.assertFalse(request.POST._mutable)
|
||||
self.assertFalse(request.data._mutable)
|
||||
|
||||
def test_multipart_without_boundary(self):
|
||||
request = WSGIRequest(
|
||||
@ -1129,6 +1161,10 @@ class RequestsTests(SimpleTestCase):
|
||||
MultiPartParserError, "Invalid boundary in multipart: None"
|
||||
):
|
||||
request.POST
|
||||
with self.assertRaisesMessage(
|
||||
MultiPartParserError, "Invalid boundary in multipart: None"
|
||||
):
|
||||
request.data
|
||||
|
||||
def test_multipart_non_ascii_content_type(self):
|
||||
request = WSGIRequest(
|
||||
@ -1145,6 +1181,8 @@ class RequestsTests(SimpleTestCase):
|
||||
)
|
||||
with self.assertRaisesMessage(MultiPartParserError, msg):
|
||||
request.POST
|
||||
with self.assertRaisesMessage(MultiPartParserError, msg):
|
||||
request.data
|
||||
|
||||
def test_multipart_with_header_fields_too_large(self):
|
||||
payload = FakePayload(
|
||||
|
Loading…
Reference in New Issue
Block a user