mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 07:57:55 +03:00 
			
		
		
		
	Cleaner content negotiation. Occurs after permissions. Optional 'force' flag.
This commit is contained in:
		
							parent
							
								
									5036638d0c
								
							
						
					
					
						commit
						6543ccd244
					
				|  | @ -1,24 +1,40 @@ | ||||||
| from djangorestframework import exceptions | from djangorestframework import exceptions | ||||||
| from djangorestframework.settings import api_settings | from djangorestframework.settings import api_settings | ||||||
| from djangorestframework.utils.mediatypes import order_by_precedence | from djangorestframework.utils.mediatypes import order_by_precedence | ||||||
|  | from django.http import Http404 | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )') | MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class BaseContentNegotiation(object): | class BaseContentNegotiation(object): | ||||||
|     def determine_renderer(self, request, renderers): |     def negotiate(self, request, renderers, format=None, force=False): | ||||||
|         raise NotImplementedError('.determine_renderer() must be implemented') |         raise NotImplementedError('.negotiate() must be implemented') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DefaultContentNegotiation(object): | class DefaultContentNegotiation(object): | ||||||
|     settings = api_settings |     settings = api_settings | ||||||
| 
 | 
 | ||||||
|     def negotiate(self, request, renderers): |     def negotiate(self, request, renderers, format=None, force=False): | ||||||
|         """ |         """ | ||||||
|         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._negotiate(request, renderers, format) | ||||||
|  |         except (Http404, exceptions.NotAcceptable): | ||||||
|  |             if force: | ||||||
|  |                 return (renderers[0], renderers[0].media_type) | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  |     def _negotiate(self, request, renderers, format=None): | ||||||
|  |         """ | ||||||
|  |         Actual implementation of negotiate, inside the 'force' wrapper. | ||||||
|  |         """ | ||||||
|  |         renderers = self.filter_renderers(renderers, format) | ||||||
|         accepts = self.get_accept_list(request) |         accepts = self.get_accept_list(request) | ||||||
| 
 | 
 | ||||||
|         # Check the acceptable media types against each renderer, |         # Check the acceptable media types against each renderer, | ||||||
|  | @ -33,6 +49,19 @@ class DefaultContentNegotiation(object): | ||||||
| 
 | 
 | ||||||
|         raise exceptions.NotAcceptable(available_renderers=renderers) |         raise exceptions.NotAcceptable(available_renderers=renderers) | ||||||
| 
 | 
 | ||||||
|  |     def filter_renderers(self, renderers, format): | ||||||
|  |         """ | ||||||
|  |         If there is a '.json' style format suffix, only use | ||||||
|  |         renderers that accept that format. | ||||||
|  |         """ | ||||||
|  |         if not format: | ||||||
|  |             return renderers | ||||||
|  | 
 | ||||||
|  |         renderers = [renderer for renderer in renderers | ||||||
|  |                      if renderer.can_handle_format(format)] | ||||||
|  |         if not renderers: | ||||||
|  |             raise Http404() | ||||||
|  | 
 | ||||||
|     def get_accept_list(self, request): |     def get_accept_list(self, request): | ||||||
|         """ |         """ | ||||||
|         Given the incoming request, return a tokenised list of |         Given the incoming request, return a tokenised list of | ||||||
|  |  | ||||||
|  | @ -197,24 +197,13 @@ class APIView(_View): | ||||||
|         """ |         """ | ||||||
|         return [throttle(self) for throttle in self.throttle_classes] |         return [throttle(self) for throttle in self.throttle_classes] | ||||||
| 
 | 
 | ||||||
|     def content_negotiation(self, request): |     def content_negotiation(self, request, force=False): | ||||||
|         """ |         """ | ||||||
|         Determine which renderer and media type to use render the response. |         Determine which renderer and media type to use render the response. | ||||||
|         """ |         """ | ||||||
|         renderers = self.get_renderers() |         renderers = self.get_renderers() | ||||||
| 
 |  | ||||||
|         if self.format: |  | ||||||
|             # If there is a '.json' style format suffix, only use |  | ||||||
|             # renderers that accept that format. |  | ||||||
|             fallback = renderers[0] |  | ||||||
|             renderers = [renderer for renderer in renderers |  | ||||||
|                          if renderer.can_handle_format(self.format)] |  | ||||||
|             if not renderers: |  | ||||||
|                 self.format404 = True |  | ||||||
|                 return (fallback, fallback.media_type) |  | ||||||
| 
 |  | ||||||
|         conneg = self.content_negotiation_class() |         conneg = self.content_negotiation_class() | ||||||
|         return conneg.negotiate(request, renderers) |         return conneg.negotiate(request, renderers, self.format, force) | ||||||
| 
 | 
 | ||||||
|     def check_permissions(self, request, obj=None): |     def check_permissions(self, request, obj=None): | ||||||
|         """ |         """ | ||||||
|  | @ -244,19 +233,17 @@ class APIView(_View): | ||||||
|         Runs anything that needs to occur prior to calling the method handlers. |         Runs anything that needs to occur prior to calling the method handlers. | ||||||
|         """ |         """ | ||||||
|         self.format = self.get_format_suffix(**kwargs) |         self.format = self.get_format_suffix(**kwargs) | ||||||
|         self.renderer, self.media_type = self.content_negotiation(request) |  | ||||||
|         self.check_permissions(request) |         self.check_permissions(request) | ||||||
|         self.check_throttles(request) |         self.check_throttles(request) | ||||||
|         # If the request included a non-existant .format URL suffix, |         self.renderer, self.media_type = self.content_negotiation(request) | ||||||
|         # raise 404, but only after first making permission checks. |  | ||||||
|         if getattr(self, 'format404', None): |  | ||||||
|             raise Http404() |  | ||||||
| 
 | 
 | ||||||
|     def finalize_response(self, request, response, *args, **kwargs): |     def finalize_response(self, request, response, *args, **kwargs): | ||||||
|         """ |         """ | ||||||
|         Returns the final response object. |         Returns the final response object. | ||||||
|         """ |         """ | ||||||
|         if isinstance(response, Response): |         if isinstance(response, Response): | ||||||
|  |             if not getattr(self, 'renderer', None): | ||||||
|  |                 self.renderer, self.media_type = self.content_negotiation(request, force=True) | ||||||
|             response.renderer = self.renderer |             response.renderer = self.renderer | ||||||
|             response.media_type = self.media_type |             response.media_type = self.media_type | ||||||
| 
 | 
 | ||||||
|  | @ -270,11 +257,7 @@ class APIView(_View): | ||||||
|         Handle any exception that occurs, by returning an appropriate response, |         Handle any exception that occurs, by returning an appropriate response, | ||||||
|         or re-raising the error. |         or re-raising the error. | ||||||
|         """ |         """ | ||||||
|         if isinstance(exc, exceptions.NotAcceptable): |         if isinstance(exc, exceptions.Throttled): | ||||||
|             # Fall back to default renderer |  | ||||||
|             self.renderer = exc.available_renderers[0] |  | ||||||
|             self.media_type = exc.available_renderers[0].media_type |  | ||||||
|         elif isinstance(exc, exceptions.Throttled): |  | ||||||
|             # Throttle wait header |             # Throttle wait header | ||||||
|             self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait |             self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user