This commit is contained in:
Si Feng 2016-11-18 03:12:59 +00:00 committed by GitHub
commit 7db2d4ef3e
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 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 = { 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 = { 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. 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 handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER_ERROR_KEY': 'detail',
'NON_FIELD_ERRORS_KEY': 'non_field_errors', 'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing # Testing

View File

@ -63,6 +63,8 @@ def exception_handler(exc, context):
Any unhandled exceptions may return `None`, which will cause a 500 error Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised. to be raised.
""" """
error_key = api_settings.EXCEPTION_HANDLER_ERROR_KEY
if isinstance(exc, exceptions.APIException): if isinstance(exc, exceptions.APIException):
headers = {} headers = {}
if getattr(exc, 'auth_header', None): if getattr(exc, 'auth_header', None):
@ -73,21 +75,21 @@ def exception_handler(exc, context):
if isinstance(exc.detail, (list, dict)): if isinstance(exc.detail, (list, dict)):
data = exc.detail data = exc.detail
else: else:
data = {'detail': exc.detail} data = {error_key: exc.detail}
set_rollback() set_rollback()
return Response(data, status=exc.status_code, headers=headers) return Response(data, status=exc.status_code, headers=headers)
elif isinstance(exc, Http404): elif isinstance(exc, Http404):
msg = _('Not found.') msg = _('Not found.')
data = {'detail': six.text_type(msg)} data = {error_key: six.text_type(msg)}
set_rollback() set_rollback()
return Response(data, status=status.HTTP_404_NOT_FOUND) return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, PermissionDenied): elif isinstance(exc, PermissionDenied):
msg = _('Permission denied.') msg = _('Permission denied.')
data = {'detail': six.text_type(msg)} data = {error_key: six.text_type(msg)}
set_rollback() set_rollback()
return Response(data, status=status.HTTP_403_FORBIDDEN) 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 import status
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.exceptions import APIException
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
@ -42,12 +43,12 @@ def basic_view(request):
class ErrorView(APIView): class ErrorView(APIView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
raise Exception raise APIException('Error!')
@api_view(['GET']) @api_view(['GET'])
def error_view(request): def error_view(request):
raise Exception raise APIException('Error!')
def sanitise_json_error(error_dict): def sanitise_json_error(error_dict):
@ -118,3 +119,32 @@ class TestCustomExceptionHandler(TestCase):
expected = 'Error!' expected = 'Error!'
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, expected) 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)