8000 Merge pull request #1092 from rouge8/custom-exception-handler-setting · lalkaka/django-rest-framework@6908c18 · GitHub
[go: up one dir, main page]

Skip to content

Commit 10000 6908c18

Browse files
committed
Merge pull request encode#1092 from rouge8/custom-exception-handler-setting
Support customizable view EXCEPTION_HANDLER (encode#907)
2 parents 916d8ab + bae0ef6 commit 6908c18

File tree

5 files changed

+80
-3
lines changed

5 files changed

+80
-3
lines changed

docs/api-guide/exceptions.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,27 @@ Might receive an error response indicating that the `DELETE` method is not allow
3030
HTTP/1.1 405 Method Not Allowed
3131
Content-Type: application/json; charset=utf-8
3232
Content-Length: 42
33-
33+
3434
{"detail": "Method 'DELETE' not allowed."}
3535

36+
## Custom exception handling
37+
38+
To implement custom exception handling (e.g. to handle additional exception classes or to override the error response format), create an exception handler function with the following signature:
39+
40+
exception_handler(exc)
41+
42+
* `exc`: The exception.
43+
44+
If the function returns `None`, a 500 error will be raised.
45+
46+
The exception handler is set globally, using the `EXCEPTION_HANDLER` setting. For example:
47+
48+
'EXCEPTION_HANDLER': 'project.app.module.function'
49+
50+
If not specified, this setting defaults to the exception handler described above:
51+
52+
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
53+
3654
---
3755

3856
# API Reference

docs/api-guide/settings.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ If you need to access the values of REST framework's API settings in your projec
2525
you should use the `api_settings` object. For example.
2626

2727
from rest_framework.settings import api_settings
28-
28+
2929
print api_settings.DEFAULT_AUTHENTICATION_CLASSES
3030

3131
The `api_settings` object will check for any user-defined settings, and otherwise fall back to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal.
@@ -339,6 +339,20 @@ Default: `'rest_framework.views.get_view_description'`
339339

340340
## Miscellaneous settings
341341

342+
#### EXCEPTION_HANDLER
343+
344+
A string representing the function that should be used when returning a response for any given exception. If the function returns `None`, a 500 error will be raised.
345+
346+
This setting can be changed to support error responses other than the default `{"detail": "Failure..."}` responses. For example, you can use it to provide API responses like `{"errors": [{"message": "Failure...", "code": ""} ...]}`.
347+
348+
This should be a function with the following signature:
349+
350+
exception_handler(exc)
351+
352+
* `exc`: The exception.
353+
354+
Default: `'rest_framework.views.exception_handler'`
355+
342356
#### FORMAT_SUFFIX_KWARG
343357

344358
The name of a parameter in the URL conf that may be used to provide a format suffix.

rest_framework/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
7878
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
7979

80+
# Exception handling
81+
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
82+
8083
# Testing
8184
'TEST_REQUEST_RENDERER_CLASSES': (
8285
'rest_framework.renderers.MultiPartRenderer',
@@ -125,6 +128,7 @@
125128
'DEFAULT_MODEL_SERIALIZER_CLASS',
126129
'DEFAULT_PAGINATION_SERIALIZER_CLASS',
127130
'DEFAULT_FILTER_BACKENDS',
131+
'EXCEPTION_HANDLER',
128132
'FILTER_BACKEND',
129133
'TEST_REQUEST_RENDERER_CLASSES',
130134
'UNAUTHENTICATED_USER',

rest_framework/tests/test_views.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ def basic_view(request):
3232
return {'method': 'PATCH', 'data': request.DATA}
3333

3434

35+
class ErrorView(APIView):
36+
def get(self, request, *args, **kwargs):
37+
raise Exception
38+
39+
40+
@api_view(['GET'])
41+
def error_view(request):
42+
raise Exception
43+
44+
3545
def sanitise_json_error(error_dict):
3646
"""
3747
Exact contents of JSON error messages depend on the installed version
@@ -99,3 +109,34 @@ def test_400_parse_error_tunneled_content(self):
99109
}
100110
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
101111
self.assertEqual(sanitise_json_error(response.data), expected)
112+
113+
114+
class TestCustomExceptionHandler(TestCase):
115+
def setUp(self):
116+
self.DEFAULT_HANDLER = api_settings.EXCEPTION_HANDLER
117+
118+
def exception_handler(exc):
119+
return Response('Error!', status=status.HTTP_400_BAD_REQUEST)
120+
121+
api_settings.EXCEPTION_HANDLER = exception_handler
122+
123+
def tearDown(self):
124+
api_settings.EXCEPTION_HANDLER = self.DEFAULT_HANDLER
125+
126+
def test_class_based_view_exception_handler(self):
127+
view = ErrorView.as_view()
128+
129+
request = factory.get('/', content_type='application/json')
130+
response = view(request)
131+
expected = 'Error!'
132+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
133+
self.assertEqual(response.data, expected)
134+
135+
def test_function_based_view_exception_handler(self):
136+
view = error_view
137+
138+
request = factory.get('/', content_type='application/json')
139+
response = view(request)
140+
expected = 'Error!'
141+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
142+
self.assertEqual(response.data, expected)

rest_framework/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ def handle_exception(self, exc):
361361
else:
362362
exc.status_code = status.HTTP_403_FORBIDDEN
363363

364-
response = exception_handler(exc)
364+
response = self.settings.EXCEPTION_HANDLER(exc)
365365

366366
if response is None:
367367
raise

0 commit comments

Comments
 (0)
0