Implement customizable exception handler

This commit is contained in:
David Avsajanishvili 2013-06-04 13:13:04 +04:00
parent c8bed35621
commit ae9ec60ce6
4 changed files with 50 additions and 35 deletions

View File

@ -0,0 +1,43 @@
"""Default handlers, configurable in settings"""
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework import status
from django.core.exceptions import PermissionDenied
from django.http import Http404
def handle_exception(view_instance, exc):
"""
Default exception handler for APIView.
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if isinstance(exc, exceptions.Throttled):
# Throttle wait header
view_instance.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = view_instance.get_authenticate_header(
view_instance.request)
if auth_header:
view_instance.headers['WWW-Authenticate'] = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
if isinstance(exc, exceptions.APIException):
return Response(exc.data, status=exc.status_code,
exception=True)
elif isinstance(exc, Http404):
return Response({'detail': 'Not found'},
status=status.HTTP_404_NOT_FOUND,
exception=True)
elif isinstance(exc, PermissionDenied):
return Response({'detail': 'Permission denied'},
status=status.HTTP_403_FORBIDDEN,
exception=True)
raise

View File

@ -59,6 +59,9 @@ DEFAULTS = {
'rest_framework.pagination.PaginationSerializer',
'DEFAULT_FILTER_BACKENDS': (),
# Exception handling
'DEFAULT_EXCEPTION_HANDLER': 'rest_framework.handlers.handle_exception',
# Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
@ -117,6 +120,7 @@ IMPORT_STRINGS = (
'FILTER_BACKEND',
'UNAUTHENTICATED_USER',
'UNAUTHENTICATED_TOKEN',
'DEFAULT_EXCEPTION_HANDLER',
)

View File

@ -343,7 +343,7 @@ class TestInstanceView(TestCase):
self.assertIn('text', response.data)
self.assertEqual(
response.data['text'],
[u'Ensure this value has at most 100 characters (it has 120).'])
['Ensure this value has at most 100 characters (it has 120).'])
def test_put_to_deleted_instance(self):
"""

View File

@ -5,8 +5,7 @@ from __future__ import unicode_literals
import warnings
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponse
from django.http import HttpResponse
from django.utils.datastructures import SortedDict
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status, exceptions
@ -26,6 +25,7 @@ class APIView(View):
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
handle_exception = api_settings.DEFAULT_EXCEPTION_HANDLER
@classmethod
def as_view(cls, **initkwargs):
@ -265,38 +265,6 @@ class APIView(View):
return response
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if isinstance(exc, exceptions.Throttled):
# Throttle wait header
self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request)
if auth_header:
self.headers['WWW-Authenticate'] = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
if isinstance(exc, exceptions.APIException):
return Response(exc.data, status=exc.status_code,
exception=True)
elif isinstance(exc, Http404):
return Response({'detail': 'Not found'},
status=status.HTTP_404_NOT_FOUND,
exception=True)
elif isinstance(exc, PermissionDenied):
return Response({'detail': 'Permission denied'},
status=status.HTTP_403_FORBIDDEN,
exception=True)
raise
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
@csrf_exempt