Commit 4a21b355 by Tom Christie

Fix fiddly content-overloading bug

parent b581ffe3
...@@ -227,16 +227,14 @@ class Request(object): ...@@ -227,16 +227,14 @@ class Request(object):
self._method = self._data[self._METHOD_PARAM].upper() self._method = self._data[self._METHOD_PARAM].upper()
self._data.pop(self._METHOD_PARAM) 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 if (self._CONTENT_PARAM and
self._CONTENTTYPE_PARAM and self._CONTENTTYPE_PARAM and
self._CONTENT_PARAM in self._data and self._CONTENT_PARAM in self._data and
self._CONTENTTYPE_PARAM in self._data): self._CONTENTTYPE_PARAM in self._data):
self._content_type = self._data[self._CONTENTTYPE_PARAM] self._content_type = self._data[self._CONTENTTYPE_PARAM]
self._stream = StringIO(self._data[self._CONTENT_PARAM]) self._stream = StringIO(self._data[self._CONTENT_PARAM])
self._data.pop(self._CONTENTTYPE_PARAM) self._data, self._files = (Empty, Empty)
self._data.pop(self._CONTENT_PARAM)
self._data, self._files = self._parse()
def _parse(self): def _parse(self):
""" """
...@@ -250,7 +248,7 @@ class Request(object): ...@@ -250,7 +248,7 @@ class Request(object):
parser = self.negotiator.select_parser(self.parsers, self.content_type) parser = self.negotiator.select_parser(self.parsers, self.content_type)
if not parser: if not parser:
raise exceptions.UnsupportedMediaType(self._content_type) raise exceptions.UnsupportedMediaType(self.content_type)
parsed = parser.parse(self.stream, meta=self.META, parsed = parser.parse(self.stream, meta=self.META,
upload_handlers=self.upload_handlers) upload_handlers=self.upload_handlers)
......
...@@ -4,6 +4,7 @@ Tests for content parsing, and form-overloaded content parsing. ...@@ -4,6 +4,7 @@ Tests for content parsing, and form-overloaded content parsing.
from django.conf.urls.defaults import patterns from django.conf.urls.defaults import patterns
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase, Client from django.test import TestCase, Client
from django.utils import simplejson as json
from rest_framework import status from rest_framework import status
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
...@@ -12,9 +13,11 @@ from rest_framework.parsers import ( ...@@ -12,9 +13,11 @@ from rest_framework.parsers import (
FormParser, FormParser,
MultiPartParser, MultiPartParser,
PlainTextParser, PlainTextParser,
JSONParser
) )
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView from rest_framework.views import APIView
...@@ -36,7 +39,7 @@ class TestMethodOverloading(TestCase): ...@@ -36,7 +39,7 @@ class TestMethodOverloading(TestCase):
POST requests can be overloaded to another method by setting a POST requests can be overloaded to another method by setting a
reserved form field 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') self.assertEqual(request.method, 'DELETE')
...@@ -117,15 +120,16 @@ class TestContentParsing(TestCase): ...@@ -117,15 +120,16 @@ class TestContentParsing(TestCase):
""" """
Ensure request.DATA returns content for overloaded POST request. Ensure request.DATA returns content for overloaded POST request.
""" """
content = 'qwerty' json_data = {'foobar': 'qwerty'}
content_type = 'text/plain' content = json.dumps(json_data)
data = { content_type = 'application/json'
Request._CONTENT_PARAM: content, form_data = {
Request._CONTENTTYPE_PARAM: content_type api_settings.FORM_CONTENT_OVERRIDE: content,
api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
} }
request = Request(factory.post('/', data)) request = Request(factory.post('/', form_data))
request.parsers = (PlainTextParser(), ) request.parsers = (JSONParser(), )
self.assertEqual(request.DATA, content) self.assertEqual(request.DATA, json_data)
# def test_accessing_post_after_data_form(self): # def test_accessing_post_after_data_form(self):
# """ # """
......
# from django.core.urlresolvers import reverse from django.test import TestCase
# from django.conf.urls.defaults import patterns, url, include from django.test.client import RequestFactory
# from django.http import HttpResponse from rest_framework import status
# from django.test import TestCase from rest_framework.decorators import api_view
# from django.utils import simplejson as json from rest_framework.response import Response
from rest_framework.settings import api_settings
# from rest_framework.views import View from rest_framework.views import APIView
factory = RequestFactory()
# class MockView(View):
# """This is a basic mock view"""
# pass class BasicView(APIView):
def get(self, request, *args, **kwargs):
return Response({'method': 'GET'})
# class MockViewFinal(View):
# """View with final() override""" def post(self, request, *args, **kwargs):
return Response({'method': 'POST', 'data': request.DATA})
# def final(self, request, response, *args, **kwargs):
# return HttpResponse('{"test": "passed"}', content_type="application/json")
@api_view(['GET', 'POST'])
def basic_view(request):
# # class ResourceMockView(View): if request.method == 'GET':
# # """This is a resource-based mock view""" return {'method': 'GET'}
elif request.method == 'POST':
# # class MockForm(forms.Form): return {'method': 'POST', 'data': request.DATA}
# # foo = forms.BooleanField(required=False)
# # bar = forms.IntegerField(help_text='Must be an integer.')
# # baz = forms.CharField(max_length=32) class ClassBasedViewIntegrationTests(TestCase):
def setUp(self):
# # form = MockForm self.view = BasicView.as_view()
def test_400_parse_error(self):
# # class MockResource(ModelResource): request = factory.post('/', 'f00bar', content_type='application/json')
# # """This is a mock model-based resource""" response = self.view(request)
expected = {
# # class MockResourceModel(models.Model): 'detail': u'JSON parse error - No JSON object could be decoded'
# # foo = models.BooleanField() }
# # bar = models.IntegerField(help_text='Must be an integer.') self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
# # baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.') self.assertEquals(response.data, expected)
# # model = MockResourceModel def test_400_parse_error_tunneled_content(self):
# # fields = ('foo', 'bar', 'baz') content = 'f00bar'
content_type = 'application/json'
# urlpatterns = patterns('', form_data = {
# url(r'^mock/$', MockView.as_view()), api_settings.FORM_CONTENT_OVERRIDE: content,
# url(r'^mock/final/$', MockViewFinal.as_view()), api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
# # url(r'^resourcemock/$', ResourceMockView.as_view()), }
# # url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)), request = factory.post('/', form_data)
# # url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)), response = self.view(request)
# url(r'^restframework/', include('rest_framework.urls', namespace='rest_framework')), expected = {
# ) 'detail': u'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
# class BaseViewTests(TestCase): self.assertEquals(response.data, expected)
# """Test the base view class of rest_framework"""
# urls = 'rest_framework.tests.views'
class FunctionBasedViewIntegrationTests(TestCase):
# def test_view_call_final(self): def setUp(self):
# response = self.client.options('/mock/final/') self.view = basic_view
# self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
# data = json.loads(response.content) def test_400_parse_error(self):
# self.assertEqual(data['test'], 'passed') request = factory.post('/', 'f00bar', content_type='application/json')
response = self.view(request)
# def test_options_method_simple_view(self): expected = {
# response = self.client.options('/mock/') 'detail': u'JSON parse error - No JSON object could be decoded'
# self._verify_options_response(response, }
# name='Mock', self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
# description='This is a basic mock view') self.assertEquals(response.data, expected)
# def test_options_method_resource_view(self): def test_400_parse_error_tunneled_content(self):
# response = self.client.options('/resourcemock/') content = 'f00bar'
# self._verify_options_response(response, content_type = 'application/json'
# name='Resource Mock', form_data = {
# description='This is a resource-based mock view', api_settings.FORM_CONTENT_OVERRIDE: content,
# fields={'foo': 'BooleanField', api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
# 'bar': 'IntegerField', }
# 'baz': 'CharField', request = factory.post('/', form_data)
# }) response = self.view(request)
expected = {
# def test_options_method_model_resource_list_view(self): 'detail': u'JSON parse error - No JSON object could be decoded'
# response = self.client.options('/model/') }
# self._verify_options_response(response, self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
# name='Mock List', self.assertEquals(response.data, expected)
# 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')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment