Fallback behavior for request parsing when request.POST already accessed. (#4500)

This commit is contained in:
Tom Christie 2016-09-21 11:49:09 +01:00 committed by GitHub
parent e82ee91078
commit be74d11165
2 changed files with 66 additions and 4 deletions

View File

@ -15,6 +15,7 @@ import sys
from django.conf import settings
from django.http import QueryDict
from django.http.multipartparser import parse_header
from django.http.request import RawPostDataException
from django.utils import six
from django.utils.datastructures import MultiValueDict
@ -263,10 +264,20 @@ class Request(object):
if content_length == 0:
self._stream = None
elif hasattr(self._request, 'read'):
elif not self._request._read_started:
self._stream = self._request
else:
self._stream = six.BytesIO(self.raw_post_data)
self._stream = six.BytesIO(self.body)
def _supports_form_parsing(self):
"""
Return True if this requests supports parsing form data.
"""
form_media = (
'application/x-www-form-urlencoded',
'multipart/form-data'
)
return any([parser.media_type in form_media for parser in self.parsers])
def _parse(self):
"""
@ -274,8 +285,18 @@ class Request(object):
May raise an `UnsupportedMediaType`, or `ParseError` exception.
"""
stream = self.stream
media_type = self.content_type
try:
stream = self.stream
except RawPostDataException:
if not hasattr(self._request, '_post'):
raise
# If request.POST has been accessed in middleware, and a method='POST'
# request was made with 'multipart/form-data', then the request stream
# will already have been exhausted.
if self._supports_form_parsing():
return (self._request.POST, self._request.FILES)
stream = None
if stream is None or media_type is None:
empty_data = QueryDict('', encoding=self._request._encoding)

View File

@ -7,11 +7,16 @@ from django import forms
from django.core.files.uploadhandler import (
MemoryFileUploadHandler, TemporaryFileUploadHandler
)
from django.http.request import RawPostDataException
from django.test import TestCase
from django.utils.six.moves import StringIO
from rest_framework.exceptions import ParseError
from rest_framework.parsers import FileUploadParser, FormParser
from rest_framework.parsers import (
FileUploadParser, FormParser, JSONParser, MultiPartParser
)
from rest_framework.request import Request
from rest_framework.test import APIRequestFactory
class Form(forms.Form):
@ -122,3 +127,39 @@ class TestFileUploadParser(TestCase):
def __replace_content_disposition(self, disposition):
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
class TestPOSTAccessed(TestCase):
def setUp(self):
self.factory = APIRequestFactory()
def test_post_accessed_in_post_method(self):
django_request = self.factory.post('/', {'foo': 'bar'})
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
django_request.POST
assert request.POST == {'foo': ['bar']}
assert request.data == {'foo': ['bar']}
def test_post_accessed_in_post_method_with_json_parser(self):
django_request = self.factory.post('/', {'foo': 'bar'})
request = Request(django_request, parsers=[JSONParser()])
django_request.POST
assert request.POST == {}
assert request.data == {}
def test_post_accessed_in_put_method(self):
django_request = self.factory.put('/', {'foo': 'bar'})
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
django_request.POST
assert request.POST == {'foo': ['bar']}
assert request.data == {'foo': ['bar']}
def test_request_read_before_parsing(self):
django_request = self.factory.put('/', {'foo': 'bar'})
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
django_request.read()
with pytest.raises(RawPostDataException):
request.POST
with pytest.raises(RawPostDataException):
request.POST
request.data