content type may be set explicitly on the response

This commit is contained in:
Tom Christie 2013-05-20 21:18:17 +01:00
parent f19e0d544f
commit aef7ac72cc
4 changed files with 27 additions and 16 deletions

View File

@ -300,6 +300,15 @@ For example:
data = serializer.data
return Response(data)
## Underspecifying the media type
In some cases you might want a renderer to serve a range of media types.
In this case you can underspecify the media types it should respond to, by using a `media_type` value such as `image/*`, or `*/*`.
If you underspecify the renderer's media type, you should make sure to specify the media type explictly when you return the response, using the `content_type` attribute. For example:
return Response(data, content_type='image/png')
## Designing your media types
For the purposes of many Web APIs, simple `JSON` responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and [HATEOAS] you'll need to consider the design and usage of your media types in more detail.

View File

@ -20,7 +20,7 @@ Unless you want to heavily customize REST framework for some reason, you should
## Response()
**Signature:** `Response(data, status=None, template_name=None, headers=None)`
**Signature:** `Response(data, status=None, template_name=None, headers=None, content_type=None)`
Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any python primatives.
@ -34,6 +34,7 @@ Arguments:
* `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes].
* `template_name`: A template name to use if `HTMLRenderer` is selected.
* `headers`: A dictionary of HTTP headers to use in the response.
* `content_type`: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.
---

View File

@ -18,7 +18,7 @@ class Response(SimpleTemplateResponse):
def __init__(self, data=None, status=200,
template_name=None, headers=None,
exception=False, charset=None):
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
@ -30,7 +30,7 @@ class Response(SimpleTemplateResponse):
self.data = data
self.template_name = template_name
self.exception = exception
self.charset = charset
self.content_type = content_type
if headers:
for name, value in six.iteritems(headers):
@ -47,18 +47,20 @@ class Response(SimpleTemplateResponse):
assert context, ".renderer_context not set on Response"
context['response'] = self
if self.charset is None:
self.charset = renderer.charset
charset = renderer.charset
content_type = self.content_type
if self.charset is not None:
content_type = "{0}; charset={1}".format(media_type, self.charset)
else:
if content_type is None and charset is not None:
content_type = "{0}; charset={1}".format(media_type, charset)
elif content_type is None:
content_type = media_type
self['Content-Type'] = content_type
ret = renderer.render(self.data, media_type, context)
if isinstance(ret, six.text_type):
return bytes(ret.encode(self.charset))
assert charset, 'renderer returned unicode, and did not specify ' \
'a charset value.'
return bytes(ret.encode(charset))
return ret
@property

View File

@ -60,11 +60,11 @@ class MockView(APIView):
return Response(DUMMYCONTENT, status=DUMMYSTATUS)
class MockViewSettingCharset(APIView):
class MockViewSettingContentType(APIView):
renderer_classes = (RendererA, RendererB, RendererC)
def get(self, request, **kwargs):
return Response(DUMMYCONTENT, status=DUMMYSTATUS, charset='setbyview')
return Response(DUMMYCONTENT, status=DUMMYSTATUS, content_type='setbyview')
class HTMLView(APIView):
@ -81,7 +81,7 @@ class HTMLView1(APIView):
return Response('text')
urlpatterns = patterns('',
url(r'^setbyview$', MockViewSettingCharset.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^setbyview$', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^html$', HTMLView.as_view()),
@ -217,11 +217,10 @@ class Issue807Testts(TestCase):
expected = "{0}; charset={1}".format(RendererC.media_type, RendererC.charset)
self.assertEqual(expected, resp['Content-Type'])
def test_charset_set_explictly_on_response(self):
def test_content_type_set_explictly_on_response(self):
"""
The charset may be set explictly on the response.
The content type may be set explictly on the response.
"""
headers = {"HTTP_ACCEPT": RendererC.media_type}
resp = self.client.get('/setbyview', **headers)
expected = "{0}; charset={1}".format(RendererC.media_type, 'setbyview')
self.assertEqual(expected, resp['Content-Type'])
self.assertEqual('setbyview', resp['Content-Type'])