Allow error key in default exception handler to be customizable

This commit is contained in:
Si Feng 2016-11-10 11:16:16 -08:00
parent 8bab7f8d58
commit 8d69b66bd2
4 changed files with 44 additions and 9 deletions

View File

@ -74,16 +74,18 @@ In order to alter the style of the response, you could write the following custo
The context argument is not used by the default handler, but can be useful if the exception handler needs further information such as the view currently being handled, which can be accessed as `context['view']`.
The exception handler must also be configured in your settings, using the `EXCEPTION_HANDLER` setting key. For example:
The exception handler and its error key must also be configured in your settings, using the `EXCEPTION_HANDLER` and `EXCEPTION_HANDLER_ERROR_KEY` setting keys. For example:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler',
'EXCEPTION_HANDLER_ERROR_KEY': 'my_error'
}
If not specified, the `'EXCEPTION_HANDLER'` setting defaults to the standard exception handler provided by REST framework:
If not specified, the `'EXCEPTION_HANDLER'` and `'EXCEPTION_HANDLER_ERROR_KEY'` settings default to the standard values provided by REST framework:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER_ERROR_KEY': 'detail'
}
Note that the exception handler will only be called for responses generated by raised exceptions. It will not be used for any responses returned directly by the view, such as the `HTTP_400_BAD_REQUEST` responses that are returned by the generic views when serializer validation fails.

View File

@ -82,6 +82,7 @@ DEFAULTS = {
# Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER_ERROR_KEY': 'detail',
'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing

View File

@ -63,6 +63,8 @@ def exception_handler(exc, context):
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
error_key = api_settings.EXCEPTION_HANDLER_ERROR_KEY
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
@ -73,21 +75,21 @@ def exception_handler(exc, context):
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = {'detail': exc.detail}
data = {error_key: exc.detail}
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
elif isinstance(exc, Http404):
msg = _('Not found.')
data = {'detail': six.text_type(msg)}
data = {error_key: six.text_type(msg)}
set_rollback()
return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, PermissionDenied):
msg = _('Permission denied.')
data = {'detail': six.text_type(msg)}
data = {error_key: six.text_type(msg)}
set_rollback()
return Response(data, status=status.HTTP_403_FORBIDDEN)

View File

@ -7,6 +7,7 @@ from django.test import TestCase
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.test import APIRequestFactory
@ -42,12 +43,12 @@ def basic_view(request):
class ErrorView(APIView):
def get(self, request, *args, **kwargs):
raise Exception
raise APIException('Error!')
@api_view(['GET'])
def error_view(request):
raise Exception
raise APIException('Error!')
def sanitise_json_error(error_dict):
@ -118,3 +119,32 @@ class TestCustomExceptionHandler(TestCase):
expected = 'Error!'
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, expected)
class TestCustomExceptionHandlerErrorKey(TestCase):
def setUp(self):
self.DEFAULT_ERROR_KEY = api_settings.EXCEPTION_HANDLER_ERROR_KEY
api_settings.EXCEPTION_HANDLER_ERROR_KEY = 'my_error'
def tearDown(self):
api_settings.EXCEPTION_HANDLER_ERROR_KEY = self.DEFAULT_ERROR_KEY
def test_class_based_view_exception_handler_error_key(self):
view = ErrorView.as_view()
request = factory.get('/', content_type='application/json')
response = view(request)
expected = {'my_error': 'Error!'}
self.assertEqual(response.status_code,
status.HTTP_500_INTERNAL_SERVER_ERROR)
self.assertEqual(response.data, expected)
def test_function_based_view_exception_handler_error_key(self):
view = error_view
request = factory.get('/', content_type='application/json')
response = view(request)
expected = {'my_error': 'Error!'}
self.assertEqual(response.status_code,
status.HTTP_500_INTERNAL_SERVER_ERROR)
self.assertEqual(response.data, expected)