Fixed #24877 -- Added middleware handling of response.render() errors.
This commit is contained in:
parent
b91a2a499f
commit
f5d5867a4a
|
@ -146,15 +146,7 @@ class BaseHandler(object):
|
||||||
try:
|
try:
|
||||||
response = wrapped_callback(request, *callback_args, **callback_kwargs)
|
response = wrapped_callback(request, *callback_args, **callback_kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If the view raised an exception, run it through exception
|
response = self.process_exception_by_middleware(e, request)
|
||||||
# middleware, and if the exception middleware returns a
|
|
||||||
# response, use that. Otherwise, reraise the exception.
|
|
||||||
for middleware_method in self._exception_middleware:
|
|
||||||
response = middleware_method(request, e)
|
|
||||||
if response:
|
|
||||||
break
|
|
||||||
if response is None:
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Complain if the view returned None (a common error).
|
# Complain if the view returned None (a common error).
|
||||||
if response is None:
|
if response is None:
|
||||||
|
@ -176,7 +168,11 @@ class BaseHandler(object):
|
||||||
"%s.process_template_response didn't return an "
|
"%s.process_template_response didn't return an "
|
||||||
"HttpResponse object. It returned None instead."
|
"HttpResponse object. It returned None instead."
|
||||||
% (middleware_method.__self__.__class__.__name__))
|
% (middleware_method.__self__.__class__.__name__))
|
||||||
response = response.render()
|
try:
|
||||||
|
response = response.render()
|
||||||
|
except Exception as e:
|
||||||
|
response = self.process_exception_by_middleware(e, request)
|
||||||
|
|
||||||
response_is_rendered = True
|
response_is_rendered = True
|
||||||
|
|
||||||
except http.Http404 as exc:
|
except http.Http404 as exc:
|
||||||
|
@ -257,6 +253,17 @@ class BaseHandler(object):
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def process_exception_by_middleware(self, exception, request):
|
||||||
|
"""
|
||||||
|
Pass the exception to the exception middleware. If no middleware
|
||||||
|
return a response for this exception, raise it.
|
||||||
|
"""
|
||||||
|
for middleware_method in self._exception_middleware:
|
||||||
|
response = middleware_method(request, exception)
|
||||||
|
if response:
|
||||||
|
return response
|
||||||
|
raise
|
||||||
|
|
||||||
def handle_uncaught_exception(self, request, resolver, exc_info):
|
def handle_uncaught_exception(self, request, resolver, exc_info):
|
||||||
"""
|
"""
|
||||||
Processing for any otherwise uncaught exceptions (those that will
|
Processing for any otherwise uncaught exceptions (those that will
|
||||||
|
|
|
@ -480,6 +480,9 @@ Requests and Responses
|
||||||
:class:`~django.template.response.TemplateResponse`, commonly used with
|
:class:`~django.template.response.TemplateResponse`, commonly used with
|
||||||
class-based views.
|
class-based views.
|
||||||
|
|
||||||
|
* Exceptions raised by the ``render()`` method are now passed to the
|
||||||
|
``process_exception()`` method of each middleware.
|
||||||
|
|
||||||
* Request middleware can now set :attr:`HttpRequest.urlconf
|
* Request middleware can now set :attr:`HttpRequest.urlconf
|
||||||
<django.http.HttpRequest.urlconf>` to ``None`` to revert any changes made
|
<django.http.HttpRequest.urlconf>` to ``None`` to revert any changes made
|
||||||
by previous middleware and return to using the :setting:`ROOT_URLCONF`.
|
by previous middleware and return to using the :setting:`ROOT_URLCONF`.
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessExceptionMiddleware(object):
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
return HttpResponse('Exception caught')
|
|
@ -486,6 +486,16 @@ class MiddlewareTests(BaseMiddlewareExceptionTest):
|
||||||
# Check that the right middleware methods have been invoked
|
# Check that the right middleware methods have been invoked
|
||||||
self.assert_middleware_usage(middleware, True, True, True, True, False)
|
self.assert_middleware_usage(middleware, True, True, True, True, False)
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
MIDDLEWARE_CLASSES=['middleware_exceptions.middleware.ProcessExceptionMiddleware'],
|
||||||
|
)
|
||||||
|
def test_exception_in_render_passed_to_process_exception(self):
|
||||||
|
# Repopulate the list of middlewares since it's already been populated
|
||||||
|
# by setUp() before the MIDDLEWARE_CLASSES setting got overridden
|
||||||
|
self.client.handler.load_middleware()
|
||||||
|
response = self.client.get('/middleware_exceptions/exception_in_render/')
|
||||||
|
self.assertEqual(response.content, b'Exception caught')
|
||||||
|
|
||||||
|
|
||||||
class BadMiddlewareTests(BaseMiddlewareExceptionTest):
|
class BadMiddlewareTests(BaseMiddlewareExceptionTest):
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ urlpatterns = [
|
||||||
url(r'^middleware_exceptions/error/$', views.server_error),
|
url(r'^middleware_exceptions/error/$', views.server_error),
|
||||||
url(r'^middleware_exceptions/null_view/$', views.null_view),
|
url(r'^middleware_exceptions/null_view/$', views.null_view),
|
||||||
url(r'^middleware_exceptions/permission_denied/$', views.permission_denied),
|
url(r'^middleware_exceptions/permission_denied/$', views.permission_denied),
|
||||||
|
url(r'^middleware_exceptions/exception_in_render/$', views.exception_in_render),
|
||||||
|
|
||||||
url(r'^middleware_exceptions/template_response/$', views.template_response),
|
url(r'^middleware_exceptions/template_response/$', views.template_response),
|
||||||
url(r'^middleware_exceptions/template_response_error/$', views.template_response_error),
|
url(r'^middleware_exceptions/template_response_error/$', views.template_response_error),
|
||||||
|
|
|
@ -32,3 +32,11 @@ def null_view(request):
|
||||||
|
|
||||||
def permission_denied(request):
|
def permission_denied(request):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
||||||
|
def exception_in_render(request):
|
||||||
|
class CustomHttpResponse(http.HttpResponse):
|
||||||
|
def render(self):
|
||||||
|
raise Exception('Exception in HttpResponse.render()')
|
||||||
|
|
||||||
|
return CustomHttpResponse('Error')
|
||||||
|
|
Loading…
Reference in New Issue