diff options
| author | Tom Christie | 2012-09-14 22:42:29 +0100 |
|---|---|---|
| committer | Tom Christie | 2012-09-14 22:42:29 +0100 |
| commit | b3e29d9576f1b1b6d12f9abfeb4a06f669b45202 (patch) | |
| tree | d89770f15bdfa6b00e4a91e2a8d0a270106065b8 /djangorestframework/contentnegotiation.py | |
| parent | b7b8cd11b1aad3fcf4bad221d164bb55e0bf5859 (diff) | |
| download | django-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.py | 63 |
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 ['*/*'] |
