mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-24 00:04:16 +03:00
Negotiation API finalized. .select_renderers and .select_parsers
This commit is contained in:
parent
bbd3728da6
commit
e126b61542
|
@ -7,3 +7,9 @@
|
||||||
> — [RFC 2616][cite], Fielding et al.
|
> — [RFC 2616][cite], Fielding et al.
|
||||||
|
|
||||||
[cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html
|
[cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html
|
||||||
|
|
||||||
|
**TODO**: Describe content negotiation style used by REST framework.
|
||||||
|
|
||||||
|
## Custom content negotiation
|
||||||
|
|
||||||
|
It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme, override `BaseContentNegotiation`, and implement the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)`
|
|
@ -4,45 +4,34 @@ from rest_framework.utils.mediatypes import order_by_precedence, media_type_matc
|
||||||
|
|
||||||
|
|
||||||
class BaseContentNegotiation(object):
|
class BaseContentNegotiation(object):
|
||||||
def negotiate(self, request, renderers, format=None, force=False):
|
def select_parser(self, request, parsers):
|
||||||
raise NotImplementedError('.negotiate() must be implemented')
|
raise NotImplementedError('.select_parser() must be implemented')
|
||||||
|
|
||||||
|
def select_renderer(self, request, renderers, format_suffix=None):
|
||||||
|
raise NotImplementedError('.select_renderer() must be implemented')
|
||||||
|
|
||||||
|
|
||||||
class DefaultContentNegotiation(object):
|
class DefaultContentNegotiation(BaseContentNegotiation):
|
||||||
settings = api_settings
|
settings = api_settings
|
||||||
|
|
||||||
def select_parser(self, parsers, media_type):
|
def select_parser(self, request, parsers):
|
||||||
"""
|
"""
|
||||||
Given a list of parsers and a media type, return the appropriate
|
Given a list of parsers and a media type, return the appropriate
|
||||||
parser to handle the incoming request.
|
parser to handle the incoming request.
|
||||||
"""
|
"""
|
||||||
for parser in parsers:
|
for parser in parsers:
|
||||||
if media_type_matches(parser.media_type, media_type):
|
if media_type_matches(parser.media_type, request.content_type):
|
||||||
return parser
|
return parser
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def negotiate(self, request, renderers, format=None, force=False):
|
def select_renderer(self, request, renderers, format_suffix=None):
|
||||||
"""
|
"""
|
||||||
Given a request and a list of renderers, return a two-tuple of:
|
Given a request and a list of renderers, return a two-tuple of:
|
||||||
(renderer, media type).
|
(renderer, media type).
|
||||||
|
|
||||||
If force is set, then suppress exceptions, and forcibly return a
|
|
||||||
fallback renderer and media_type.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.unforced_negotiate(request, renderers, format)
|
|
||||||
except (exceptions.InvalidFormat, exceptions.NotAcceptable):
|
|
||||||
if force:
|
|
||||||
return (renderers[0], renderers[0].media_type)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def unforced_negotiate(self, request, renderers, format=None):
|
|
||||||
"""
|
|
||||||
As `.negotiate()`, but does not take the optional `force` agument,
|
|
||||||
or suppress exceptions.
|
|
||||||
"""
|
"""
|
||||||
# Allow URL style format override. eg. "?format=json
|
# Allow URL style format override. eg. "?format=json
|
||||||
format = format or request.GET.get(self.settings.URL_FORMAT_OVERRIDE)
|
format_query_param = self.settings.URL_FORMAT_OVERRIDE
|
||||||
|
format = format_suffix or request.GET.get(format_query_param)
|
||||||
|
|
||||||
if format:
|
if format:
|
||||||
renderers = self.filter_renderers(renderers, format)
|
renderers = self.filter_renderers(renderers, format)
|
||||||
|
|
|
@ -266,7 +266,7 @@ class Request(object):
|
||||||
if stream is None or media_type is None:
|
if stream is None or media_type is None:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
parser = self.negotiator.select_parser(self.parsers, media_type)
|
parser = self.negotiator.select_parser(self, self.parsers)
|
||||||
|
|
||||||
if not parser:
|
if not parser:
|
||||||
raise exceptions.UnsupportedMediaType(media_type)
|
raise exceptions.UnsupportedMediaType(media_type)
|
||||||
|
|
|
@ -18,20 +18,20 @@ class TestAcceptedMediaType(TestCase):
|
||||||
self.renderers = [MockJSONRenderer(), MockHTMLRenderer()]
|
self.renderers = [MockJSONRenderer(), MockHTMLRenderer()]
|
||||||
self.negotiator = DefaultContentNegotiation()
|
self.negotiator = DefaultContentNegotiation()
|
||||||
|
|
||||||
def negotiate(self, request):
|
def select_renderer(self, request):
|
||||||
return self.negotiator.negotiate(request, self.renderers)
|
return self.negotiator.select_renderer(request, self.renderers)
|
||||||
|
|
||||||
def test_client_without_accept_use_renderer(self):
|
def test_client_without_accept_use_renderer(self):
|
||||||
request = factory.get('/')
|
request = factory.get('/')
|
||||||
accepted_renderer, accepted_media_type = self.negotiate(request)
|
accepted_renderer, accepted_media_type = self.select_renderer(request)
|
||||||
self.assertEquals(accepted_media_type, 'application/json')
|
self.assertEquals(accepted_media_type, 'application/json')
|
||||||
|
|
||||||
def test_client_underspecifies_accept_use_renderer(self):
|
def test_client_underspecifies_accept_use_renderer(self):
|
||||||
request = factory.get('/', HTTP_ACCEPT='*/*')
|
request = factory.get('/', HTTP_ACCEPT='*/*')
|
||||||
accepted_renderer, accepted_media_type = self.negotiate(request)
|
accepted_renderer, accepted_media_type = self.select_renderer(request)
|
||||||
self.assertEquals(accepted_media_type, 'application/json')
|
self.assertEquals(accepted_media_type, 'application/json')
|
||||||
|
|
||||||
def test_client_overspecifies_accept_use_client(self):
|
def test_client_overspecifies_accept_use_client(self):
|
||||||
request = factory.get('/', HTTP_ACCEPT='application/json; indent=8')
|
request = factory.get('/', HTTP_ACCEPT='application/json; indent=8')
|
||||||
accepted_renderer, accepted_media_type = self.negotiate(request)
|
accepted_renderer, accepted_media_type = self.select_renderer(request)
|
||||||
self.assertEquals(accepted_media_type, 'application/json; indent=8')
|
self.assertEquals(accepted_media_type, 'application/json; indent=8')
|
||||||
|
|
|
@ -238,7 +238,13 @@ class APIView(View):
|
||||||
"""
|
"""
|
||||||
renderers = self.get_renderers()
|
renderers = self.get_renderers()
|
||||||
conneg = self.get_content_negotiator()
|
conneg = self.get_content_negotiator()
|
||||||
return conneg.negotiate(request, renderers, self.format_kwarg, force)
|
|
||||||
|
try:
|
||||||
|
return conneg.select_renderer(request, renderers, self.format_kwarg)
|
||||||
|
except:
|
||||||
|
if force:
|
||||||
|
return (renderers[0], renderers[0].media_type)
|
||||||
|
raise
|
||||||
|
|
||||||
def has_permission(self, request, obj=None):
|
def has_permission(self, request, obj=None):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user