aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework/contentnegotiation.py
diff options
context:
space:
mode:
authorTom Christie2012-09-14 22:42:29 +0100
committerTom Christie2012-09-14 22:42:29 +0100
commitb3e29d9576f1b1b6d12f9abfeb4a06f669b45202 (patch)
treed89770f15bdfa6b00e4a91e2a8d0a270106065b8 /djangorestframework/contentnegotiation.py
parentb7b8cd11b1aad3fcf4bad221d164bb55e0bf5859 (diff)
downloaddjango-rest-framework-b3e29d9576f1b1b6d12f9abfeb4a06f669b45202.tar.bz2
Moved content negotiation out of response. Nicer exception handling now.
Diffstat (limited to 'djangorestframework/contentnegotiation.py')
-rw-r--r--djangorestframework/contentnegotiation.py63
1 files changed, 63 insertions, 0 deletions
diff --git a/djangorestframework/contentnegotiation.py b/djangorestframework/contentnegotiation.py
new file mode 100644
index 00000000..223919ef
--- /dev/null
+++ b/djangorestframework/contentnegotiation.py
@@ -0,0 +1,63 @@
+from djangorestframework import exceptions
+from djangorestframework.settings import api_settings
+from djangorestframework.utils.mediatypes import order_by_precedence
+import re
+
+MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
+
+
+class BaseContentNegotiation(object):
+ def determine_renderer(self, request, renderers):
+ raise NotImplementedError('.determine_renderer() must be implemented')
+
+
+class DefaultContentNegotiation(object):
+ settings = api_settings
+
+ def negotiate(self, request, renderers):
+ """
+ Given a request and a list of renderers, return a two-tuple of:
+ (renderer, media type).
+ """
+ accepts = self.get_accept_list(request)
+
+ # 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_set in order_by_precedence(accepts):
+ for renderer in renderers:
+ for media_type in media_type_set:
+ if renderer.can_handle_media_type(media_type):
+ return renderer, media_type
+
+ raise exceptions.NotAcceptable(available_renderers=renderers)
+
+ def get_accept_list(self, request):
+ """
+ Given the incoming request, return a tokenised list of
+ media type strings.
+ """
+ if self.settings.URL_ACCEPT_OVERRIDE:
+ # URL style accept override. eg. "?accept=application/json"
+ override = request.GET.get(self.settings.URL_ACCEPT_OVERRIDE)
+ if override:
+ return [override]
+
+ if (self.settings.IGNORE_MSIE_ACCEPT_HEADER and
+ 'HTTP_USER_AGENT' in request.META and
+ MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
+ request.META.get('HTTP_X_REQUESTED_WITH', '').lower() != 'xmlhttprequest'):
+ # Ignore MSIE's broken accept behavior except for AJAX requests
+ # and do something sensible instead
+ return ['text/html', '*/*']
+
+ if 'HTTP_ACCEPT' in request.META:
+ # Standard HTTP Accept negotiation
+ # Accept header specified
+ tokens = request.META['HTTP_ACCEPT'].split(',')
+ return [token.strip() for token in tokens]
+
+ # Standard HTTP Accept negotiation
+ # No accept header specified
+ return ['*/*']