From d9a41859208e5dfa3860704b764f083e398182e0 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 18 Nov 2015 00:00:29 +0100 Subject: [PATCH 1/2] Handle empty body with json content-type as json An empty request body should not imply HTML form semantics. An attribute in a JSON object can either be: - not set at all, - null, - an empty list. An HTML form isn't as flexible, so invariably turning an empty body into a QueryDict makes it impossible to determine if a given value was passed in as an empty list or if it was not set at all. --- rest_framework/parsers.py | 3 +++ rest_framework/request.py | 2 +- tests/test_request.py | 12 +++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 2dc808f1a..d99fef97b 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -62,6 +62,9 @@ class JSONParser(BaseParser): parser_context = parser_context or {} encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + if not stream: + return {} + try: data = stream.read().decode(encoding) return json.loads(data) diff --git a/rest_framework/request.py b/rest_framework/request.py index 846cadcd9..785abd775 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -275,7 +275,7 @@ class Request(object): stream = self.stream media_type = self.content_type - if stream is None or media_type is None: + if stream is None and not media_type: empty_data = QueryDict('', encoding=self._request._encoding) empty_files = MultiValueDict() return (empty_data, empty_files) diff --git a/tests/test_request.py b/tests/test_request.py index 24be368b3..c7e812b8c 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -14,7 +14,7 @@ from django.utils import six from rest_framework import status from rest_framework.authentication import SessionAuthentication -from rest_framework.parsers import BaseParser, FormParser, MultiPartParser +from rest_framework.parsers import BaseParser, FormParser, JSONParser, MultiPartParser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.test import APIClient, APIRequestFactory @@ -51,6 +51,16 @@ class TestContentParsing(TestCase): request = Request(factory.head('/')) self.assertEqual(request.data, {}) + def test_empty_body_yields_empty_dict_for_json_POST(self): + """ + Ensure request.data returns empty dict for POST request with JSON + content type. + """ + request = Request(factory.post('/', CONTENT_TYPE='application/json')) + request.parsers = (JSONParser(),) + self.assertEquals(type(request.data), dict) + self.assertEquals(request.data, {}) + def test_request_DATA_with_form_content(self): """ Ensure request.data returns content for POST request with form content. From 2f5dc0a89940cfd36d2d710469da73c34a12c12b Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 18 Nov 2015 08:41:08 +0100 Subject: [PATCH 2/2] Import line too long --- tests/test_request.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_request.py b/tests/test_request.py index c7e812b8c..7c92050db 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -14,7 +14,9 @@ from django.utils import six from rest_framework import status from rest_framework.authentication import SessionAuthentication -from rest_framework.parsers import BaseParser, FormParser, JSONParser, MultiPartParser +from rest_framework.parsers import ( + BaseParser, FormParser, JSONParser, MultiPartParser +) from rest_framework.request import Request from rest_framework.response import Response from rest_framework.test import APIClient, APIRequestFactory