aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
authorTom Christie2012-10-08 17:10:50 +0100
committerTom Christie2012-10-08 17:10:50 +0100
commit4a21b3557edb3b901b86d3a888c44f772e33b922 (patch)
treeb54cc491fffd9c7ddd90a414112072aa5562222e /rest_framework
parentb581ffe323d88b6740abfed0fd552cc436fd2dcc (diff)
downloaddjango-rest-framework-4a21b3557edb3b901b86d3a888c44f772e33b922.tar.bz2
Fix fiddly content-overloading bug
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/request.py8
-rw-r--r--rest_framework/tests/request.py22
-rw-r--r--rest_framework/tests/views.py211
3 files changed, 99 insertions, 142 deletions
diff --git a/rest_framework/request.py b/rest_framework/request.py
index ac15defc..3ce93181 100644
--- a/rest_framework/request.py
+++ b/rest_framework/request.py
@@ -227,16 +227,14 @@ class Request(object):
self._method = self._data[self._METHOD_PARAM].upper()
self._data.pop(self._METHOD_PARAM)
- # Content overloading - modify the content type, and re-parse.
+ # Content overloading - modify the content type, and force re-parse.
if (self._CONTENT_PARAM and
self._CONTENTTYPE_PARAM and
self._CONTENT_PARAM in self._data and
self._CONTENTTYPE_PARAM in self._data):
self._content_type = self._data[self._CONTENTTYPE_PARAM]
self._stream = StringIO(self._data[self._CONTENT_PARAM])
- self._data.pop(self._CONTENTTYPE_PARAM)
- self._data.pop(self._CONTENT_PARAM)
- self._data, self._files = self._parse()
+ self._data, self._files = (Empty, Empty)
def _parse(self):
"""
@@ -250,7 +248,7 @@ class Request(object):
parser = self.negotiator.select_parser(self.parsers, self.content_type)
if not parser:
- raise exceptions.UnsupportedMediaType(self._content_type)
+ raise exceptions.UnsupportedMediaType(self.content_type)
parsed = parser.parse(self.stream, meta=self.META,
upload_handlers=self.upload_handlers)
diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py
index f5c63f11..7b24b036 100644
--- a/rest_framework/tests/request.py
+++ b/rest_framework/tests/request.py
@@ -4,6 +4,7 @@ Tests for content parsing, and form-overloaded content parsing.
from django.conf.urls.defaults import patterns
from django.contrib.auth.models import User
from django.test import TestCase, Client
+from django.utils import simplejson as json
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
@@ -12,9 +13,11 @@ from rest_framework.parsers import (
FormParser,
MultiPartParser,
PlainTextParser,
+ JSONParser
)
from rest_framework.request import Request
from rest_framework.response import Response
+from rest_framework.settings import api_settings
from rest_framework.views import APIView
@@ -36,7 +39,7 @@ class TestMethodOverloading(TestCase):
POST requests can be overloaded to another method by setting a
reserved form field
"""
- request = Request(factory.post('/', {Request._METHOD_PARAM: 'DELETE'}))
+ request = Request(factory.post('/', {api_settings.FORM_METHOD_OVERRIDE: 'DELETE'}))
self.assertEqual(request.method, 'DELETE')
@@ -117,15 +120,16 @@ class TestContentParsing(TestCase):
"""
Ensure request.DATA returns content for overloaded POST request.
"""
- content = 'qwerty'
- content_type = 'text/plain'
- data = {
- Request._CONTENT_PARAM: content,
- Request._CONTENTTYPE_PARAM: content_type
+ json_data = {'foobar': 'qwerty'}
+ content = json.dumps(json_data)
+ content_type = 'application/json'
+ form_data = {
+ api_settings.FORM_CONTENT_OVERRIDE: content,
+ api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
}
- request = Request(factory.post('/', data))
- request.parsers = (PlainTextParser(), )
- self.assertEqual(request.DATA, content)
+ request = Request(factory.post('/', form_data))
+ request.parsers = (JSONParser(), )
+ self.assertEqual(request.DATA, json_data)
# def test_accessing_post_after_data_form(self):
# """
diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py
index cd1f73c3..61b288c6 100644
--- a/rest_framework/tests/views.py
+++ b/rest_framework/tests/views.py
@@ -1,128 +1,83 @@
-# from django.core.urlresolvers import reverse
-# from django.conf.urls.defaults import patterns, url, include
-# from django.http import HttpResponse
-# from django.test import TestCase
-# from django.utils import simplejson as json
-
-# from rest_framework.views import View
-
-
-# class MockView(View):
-# """This is a basic mock view"""
-# pass
-
-
-# class MockViewFinal(View):
-# """View with final() override"""
-
-# def final(self, request, response, *args, **kwargs):
-# return HttpResponse('{"test": "passed"}', content_type="application/json")
-
-
-# # class ResourceMockView(View):
-# # """This is a resource-based mock view"""
-
-# # class MockForm(forms.Form):
-# # foo = forms.BooleanField(required=False)
-# # bar = forms.IntegerField(help_text='Must be an integer.')
-# # baz = forms.CharField(max_length=32)
-
-# # form = MockForm
-
-
-# # class MockResource(ModelResource):
-# # """This is a mock model-based resource"""
-
-# # class MockResourceModel(models.Model):
-# # foo = models.BooleanField()
-# # bar = models.IntegerField(help_text='Must be an integer.')
-# # baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
-
-# # model = MockResourceModel
-# # fields = ('foo', 'bar', 'baz')
-
-# urlpatterns = patterns('',
-# url(r'^mock/$', MockView.as_view()),
-# url(r'^mock/final/$', MockViewFinal.as_view()),
-# # url(r'^resourcemock/$', ResourceMockView.as_view()),
-# # url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)),
-# # url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),
-# url(r'^restframework/', include('rest_framework.urls', namespace='rest_framework')),
-# )
-
-
-# class BaseViewTests(TestCase):
-# """Test the base view class of rest_framework"""
-# urls = 'rest_framework.tests.views'
-
-# def test_view_call_final(self):
-# response = self.client.options('/mock/final/')
-# self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
-# data = json.loads(response.content)
-# self.assertEqual(data['test'], 'passed')
-
-# def test_options_method_simple_view(self):
-# response = self.client.options('/mock/')
-# self._verify_options_response(response,
-# name='Mock',
-# description='This is a basic mock view')
-
-# def test_options_method_resource_view(self):
-# response = self.client.options('/resourcemock/')
-# self._verify_options_response(response,
-# name='Resource Mock',
-# description='This is a resource-based mock view',
-# fields={'foo': 'BooleanField',
-# 'bar': 'IntegerField',
-# 'baz': 'CharField',
-# })
-
-# def test_options_method_model_resource_list_view(self):
-# response = self.client.options('/model/')
-# self._verify_options_response(response,
-# name='Mock List',
-# description='This is a mock model-based resource',
-# fields={'foo': 'BooleanField',
-# 'bar': 'IntegerField',
-# 'baz': 'CharField',
-# })
-
-# def test_options_method_model_resource_detail_view(self):
-# response = self.client.options('/model/0/')
-# self._verify_options_response(response,
-# name='Mock Instance',
-# description='This is a mock model-based resource',
-# fields={'foo': 'BooleanField',
-# 'bar': 'IntegerField',
-# 'baz': 'CharField',
-# })
-
-# def _verify_options_response(self, response, name, description, fields=None, status=200,
-# mime_type='application/json'):
-# self.assertEqual(response.status_code, status)
-# self.assertEqual(response['Content-Type'].split(';')[0], mime_type)
-# data = json.loads(response.content)
-# self.assertTrue('application/json' in data['renders'])
-# self.assertEqual(name, data['name'])
-# self.assertEqual(description, data['description'])
-# if fields is None:
-# self.assertFalse(hasattr(data, 'fields'))
-# else:
-# self.assertEqual(data['fields'], fields)
-
-
-# class ExtraViewsTests(TestCase):
-# """Test the extra views rest_framework provides"""
-# urls = 'rest_framework.tests.views'
-
-# def test_login_view(self):
-# """Ensure the login view exists"""
-# response = self.client.get(reverse('rest_framework:login'))
-# self.assertEqual(response.status_code, 200)
-# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
-
-# def test_logout_view(self):
-# """Ensure the logout view exists"""
-# response = self.client.get(reverse('rest_framework:logout'))
-# self.assertEqual(response.status_code, 200)
-# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
+from django.test import TestCase
+from django.test.client import RequestFactory
+from rest_framework import status
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.settings import api_settings
+from rest_framework.views import APIView
+
+factory = RequestFactory()
+
+
+class BasicView(APIView):
+ def get(self, request, *args, **kwargs):
+ return Response({'method': 'GET'})
+
+ def post(self, request, *args, **kwargs):
+ return Response({'method': 'POST', 'data': request.DATA})
+
+
+@api_view(['GET', 'POST'])
+def basic_view(request):
+ if request.method == 'GET':
+ return {'method': 'GET'}
+ elif request.method == 'POST':
+ return {'method': 'POST', 'data': request.DATA}
+
+
+class ClassBasedViewIntegrationTests(TestCase):
+ def setUp(self):
+ self.view = BasicView.as_view()
+
+ def test_400_parse_error(self):
+ request = factory.post('/', 'f00bar', content_type='application/json')
+ response = self.view(request)
+ expected = {
+ 'detail': u'JSON parse error - No JSON object could be decoded'
+ }
+ self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEquals(response.data, expected)
+
+ def test_400_parse_error_tunneled_content(self):
+ content = 'f00bar'
+ content_type = 'application/json'
+ form_data = {
+ api_settings.FORM_CONTENT_OVERRIDE: content,
+ api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
+ }
+ request = factory.post('/', form_data)
+ response = self.view(request)
+ expected = {
+ 'detail': u'JSON parse error - No JSON object could be decoded'
+ }
+ self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEquals(response.data, expected)
+
+
+class FunctionBasedViewIntegrationTests(TestCase):
+ def setUp(self):
+ self.view = basic_view
+
+ def test_400_parse_error(self):
+ request = factory.post('/', 'f00bar', content_type='application/json')
+ response = self.view(request)
+ expected = {
+ 'detail': u'JSON parse error - No JSON object could be decoded'
+ }
+ self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEquals(response.data, expected)
+
+ def test_400_parse_error_tunneled_content(self):
+ content = 'f00bar'
+ content_type = 'application/json'
+ form_data = {
+ api_settings.FORM_CONTENT_OVERRIDE: content,
+ api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
+ }
+ request = factory.post('/', form_data)
+ response = self.view(request)
+ expected = {
+ 'detail': u'JSON parse error - No JSON object could be decoded'
+ }
+ self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEquals(response.data, expected)