2011-05-19 21:36:30 +04:00
|
|
|
"""
|
2012-02-07 15:15:30 +04:00
|
|
|
The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes.
|
|
|
|
|
|
|
|
`Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned
|
2012-02-07 18:22:14 +04:00
|
|
|
from any view. It is a bit smarter than Django's `HttpResponse`, for it renders automatically
|
|
|
|
its content to a serial format by using a list of :mod:`renderers`.
|
2012-02-07 15:15:30 +04:00
|
|
|
|
2012-02-07 18:22:14 +04:00
|
|
|
To determine the content type to which it must render, default behaviour is to use standard
|
2012-02-20 13:36:03 +04:00
|
|
|
HTTP Accept header content negotiation. But `Response` also supports overriding the content type
|
2012-02-07 18:22:14 +04:00
|
|
|
by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers
|
|
|
|
from Internet Explorer user agents and use a sensible browser `Accept` header instead.
|
2012-02-07 15:15:30 +04:00
|
|
|
|
|
|
|
|
|
|
|
`ImmediateResponse` is an exception that inherits from `Response`. It can be used
|
2012-02-20 13:36:03 +04:00
|
|
|
to abort the request handling (i.e. ``View.get``, ``View.put``, ...),
|
2012-02-07 15:15:30 +04:00
|
|
|
and immediately returning a response.
|
2011-05-19 21:36:30 +04:00
|
|
|
"""
|
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
from django.template.response import SimpleTemplateResponse
|
2011-01-24 02:08:16 +03:00
|
|
|
from django.core.handlers.wsgi import STATUS_CODE_TEXT
|
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
from djangorestframework.utils.mediatypes import order_by_precedence
|
|
|
|
from djangorestframework.utils import MSIE_USER_AGENT_REGEX
|
|
|
|
from djangorestframework import status
|
|
|
|
|
2011-01-24 02:08:16 +03:00
|
|
|
|
2012-02-07 15:15:30 +04:00
|
|
|
__all__ = ('Response', 'ImmediateResponse')
|
2011-01-24 02:08:16 +03:00
|
|
|
|
2012-01-21 22:33:34 +04:00
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
class Response(SimpleTemplateResponse):
|
2011-05-10 19:01:58 +04:00
|
|
|
"""
|
|
|
|
An HttpResponse that may include content that hasn't yet been serialized.
|
2012-02-07 18:22:14 +04:00
|
|
|
|
2012-02-20 13:36:03 +04:00
|
|
|
Kwargs:
|
|
|
|
- content(object). The raw content, not yet serialized. This must be simple Python
|
2012-02-07 18:22:14 +04:00
|
|
|
data that renderers can handle (e.g.: `dict`, `str`, ...)
|
|
|
|
- renderers(list/tuple). The renderers to use for rendering the response content.
|
2011-05-10 19:01:58 +04:00
|
|
|
"""
|
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
_ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params
|
|
|
|
_IGNORE_IE_ACCEPT_HEADER = True
|
|
|
|
|
|
|
|
def __init__(self, content=None, status=None, request=None, renderers=None):
|
|
|
|
# First argument taken by `SimpleTemplateResponse.__init__` is template_name,
|
|
|
|
# which we don't need
|
|
|
|
super(Response, self).__init__(None, status=status)
|
2012-02-07 15:15:30 +04:00
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
# We need to store our content in raw content to avoid overriding HttpResponse's
|
|
|
|
# `content` property
|
2012-02-20 13:36:03 +04:00
|
|
|
self.raw_content = content
|
2011-04-11 19:38:00 +04:00
|
|
|
self.has_content_body = content is not None
|
2012-02-02 20:19:44 +04:00
|
|
|
self.request = request
|
|
|
|
if renderers is not None:
|
|
|
|
self.renderers = renderers
|
|
|
|
|
|
|
|
@property
|
|
|
|
def rendered_content(self):
|
|
|
|
"""
|
2012-02-20 13:36:03 +04:00
|
|
|
The final rendered content. Accessing this attribute triggers the complete rendering cycle :
|
2012-02-07 15:15:30 +04:00
|
|
|
selecting suitable renderer, setting response's actual content type, rendering data.
|
2012-02-02 20:19:44 +04:00
|
|
|
"""
|
|
|
|
renderer, media_type = self._determine_renderer()
|
|
|
|
|
|
|
|
# Set the media type of the response
|
|
|
|
self['Content-Type'] = renderer.media_type
|
|
|
|
|
|
|
|
# Render the response content
|
|
|
|
if self.has_content_body:
|
|
|
|
return renderer.render(self.raw_content, media_type)
|
|
|
|
return renderer.render()
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2011-01-24 02:08:16 +03:00
|
|
|
@property
|
|
|
|
def status_text(self):
|
2011-05-10 19:01:58 +04:00
|
|
|
"""
|
2012-02-07 15:15:30 +04:00
|
|
|
Returns reason text corresponding to our HTTP response status code.
|
2011-05-10 19:01:58 +04:00
|
|
|
Provided for convenience.
|
|
|
|
"""
|
2012-02-14 12:05:28 +04:00
|
|
|
return STATUS_CODE_TEXT.get(self.status_code, '')
|
2011-01-24 02:08:16 +03:00
|
|
|
|
2012-02-02 20:19:44 +04:00
|
|
|
def _determine_accept_list(self):
|
2012-02-07 15:15:30 +04:00
|
|
|
"""
|
|
|
|
Returns a list of accepted media types. This list is determined from :
|
2012-02-20 13:36:03 +04:00
|
|
|
|
2012-02-07 15:15:30 +04:00
|
|
|
1. overload with `_ACCEPT_QUERY_PARAM`
|
2012-02-20 13:36:03 +04:00
|
|
|
2. `Accept` header of the request
|
2012-02-07 15:15:30 +04:00
|
|
|
|
|
|
|
If those are useless, a default value is returned instead.
|
|
|
|
"""
|
2012-02-02 20:19:44 +04:00
|
|
|
request = self.request
|
|
|
|
if request is None:
|
|
|
|
return ['*/*']
|
|
|
|
|
|
|
|
if self._ACCEPT_QUERY_PARAM and request.GET.get(self._ACCEPT_QUERY_PARAM, None):
|
|
|
|
# Use _accept parameter override
|
|
|
|
return [request.GET.get(self._ACCEPT_QUERY_PARAM)]
|
|
|
|
elif (self._IGNORE_IE_ACCEPT_HEADER and
|
|
|
|
'HTTP_USER_AGENT' in request.META and
|
|
|
|
MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])):
|
|
|
|
# Ignore MSIE's broken accept behavior and do something sensible instead
|
|
|
|
return ['text/html', '*/*']
|
|
|
|
elif 'HTTP_ACCEPT' in request.META:
|
|
|
|
# Use standard HTTP Accept negotiation
|
|
|
|
return [token.strip() for token in request.META['HTTP_ACCEPT'].split(',')]
|
|
|
|
else:
|
|
|
|
# No accept header specified
|
|
|
|
return ['*/*']
|
|
|
|
|
|
|
|
def _determine_renderer(self):
|
|
|
|
"""
|
2012-02-07 15:15:30 +04:00
|
|
|
Determines the appropriate renderer for the output, given the list of accepted media types,
|
2012-02-02 20:19:44 +04:00
|
|
|
and the :attr:`renderers` set on this class.
|
|
|
|
|
|
|
|
Returns a 2-tuple of `(renderer, media_type)`
|
|
|
|
|
|
|
|
See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
|
|
"""
|
|
|
|
# Check the acceptable media types against each renderer,
|
|
|
|
# attempting more specific media types first
|
|
|
|
# NB. The inner loop here isn't as bad as it first looks :)
|
|
|
|
# Worst case is we're looping over len(accept_list) * len(self.renderers)
|
|
|
|
for media_type_list in order_by_precedence(self._determine_accept_list()):
|
2012-02-07 15:15:30 +04:00
|
|
|
for renderer in self.renderers:
|
2012-02-02 20:19:44 +04:00
|
|
|
for media_type in media_type_list:
|
|
|
|
if renderer.can_handle_response(media_type):
|
|
|
|
return renderer, media_type
|
|
|
|
|
|
|
|
# No acceptable renderers were found
|
2012-02-10 12:18:39 +04:00
|
|
|
raise ImmediateResponse({'detail': 'Could not satisfy the client\'s Accept header',
|
2012-02-02 20:19:44 +04:00
|
|
|
'available_types': self._rendered_media_types},
|
|
|
|
status=status.HTTP_406_NOT_ACCEPTABLE,
|
|
|
|
renderers=self.renderers)
|
|
|
|
|
|
|
|
def _get_renderers(self):
|
|
|
|
if hasattr(self, '_renderers'):
|
|
|
|
return self._renderers
|
|
|
|
return ()
|
|
|
|
|
|
|
|
def _set_renderers(self, value):
|
|
|
|
self._renderers = value
|
|
|
|
|
|
|
|
renderers = property(_get_renderers, _set_renderers)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _rendered_media_types(self):
|
|
|
|
"""
|
|
|
|
Return an list of all the media types that this response can render.
|
|
|
|
"""
|
|
|
|
return [renderer.media_type for renderer in self.renderers]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _rendered_formats(self):
|
|
|
|
"""
|
|
|
|
Return a list of all the formats that this response can render.
|
|
|
|
"""
|
|
|
|
return [renderer.format for renderer in self.renderers]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _default_renderer(self):
|
|
|
|
"""
|
|
|
|
Return the response's default renderer class.
|
|
|
|
"""
|
|
|
|
return self.renderers[0]
|
|
|
|
|
2011-01-24 02:08:16 +03:00
|
|
|
|
2012-02-14 12:10:04 +04:00
|
|
|
class ImmediateResponse(Response, Exception):
|
2011-05-10 19:01:58 +04:00
|
|
|
"""
|
2012-02-07 15:15:30 +04:00
|
|
|
A subclass of :class:`Response` used to abort the current request handling.
|
2011-05-10 19:01:58 +04:00
|
|
|
"""
|
|
|
|
|
2012-02-14 12:05:28 +04:00
|
|
|
def __str__(self):
|
|
|
|
"""
|
|
|
|
Since this class is also an exception it has to provide a sensible
|
|
|
|
representation for the cases when it is treated as an exception.
|
|
|
|
"""
|
|
|
|
return ('%s must be caught in try/except block, '
|
|
|
|
'and returned as a normal HttpResponse' % self.__class__.__name__)
|