mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-06 21:40:13 +03:00
Allow error key in default exception handler to be customizable
This commit is contained in:
parent
8bab7f8d58
commit
8d69b66bd2
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user