summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Fankhauser <sylvain.fankhauser@liip.ch>2015-06-05 15:30:03 +0200
committerTim Graham <timograham@gmail.com>2015-07-03 12:06:40 -0400
commitf5d5867a4a6aeddd58ff855a01ab4e438d938ac1 (patch)
tree73ecdbab94868fbd360f057d4dc192781bebfae6
parentb91a2a499fd562011fd275238924baa6002fb1f8 (diff)
Fixed #24877 -- Added middleware handling of response.render() errors.
-rw-r--r--django/core/handlers/base.py27
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--tests/middleware_exceptions/middleware.py8
-rw-r--r--tests/middleware_exceptions/tests.py10
-rw-r--r--tests/middleware_exceptions/urls.py1
-rw-r--r--tests/middleware_exceptions/views.py8
6 files changed, 47 insertions, 10 deletions
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 628f45df9e..5d25b89448 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -146,15 +146,7 @@ class BaseHandler(object):
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
except Exception as e:
- # If the view raised an exception, run it through exception
- # 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
+ response = self.process_exception_by_middleware(e, request)
# Complain if the view returned None (a common error).
if response is None:
@@ -176,7 +168,11 @@ class BaseHandler(object):
"%s.process_template_response didn't return an "
"HttpResponse object. It returned None instead."
% (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
except http.Http404 as exc:
@@ -257,6 +253,17 @@ class BaseHandler(object):
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):
"""
Processing for any otherwise uncaught exceptions (those that will
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 75c126c38b..b5c515db09 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -480,6 +480,9 @@ Requests and Responses
:class:`~django.template.response.TemplateResponse`, commonly used with
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
<django.http.HttpRequest.urlconf>` to ``None`` to revert any changes made
by previous middleware and return to using the :setting:`ROOT_URLCONF`.
diff --git a/tests/middleware_exceptions/middleware.py b/tests/middleware_exceptions/middleware.py
new file mode 100644
index 0000000000..944dd8910d
--- /dev/null
+++ b/tests/middleware_exceptions/middleware.py
@@ -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')
diff --git a/tests/middleware_exceptions/tests.py b/tests/middleware_exceptions/tests.py
index eb80460bce..11769277b8 100644
--- a/tests/middleware_exceptions/tests.py
+++ b/tests/middleware_exceptions/tests.py
@@ -486,6 +486,16 @@ class MiddlewareTests(BaseMiddlewareExceptionTest):
# Check that the right middleware methods have been invoked
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):
diff --git a/tests/middleware_exceptions/urls.py b/tests/middleware_exceptions/urls.py
index 6fde4fd942..85c7a785ec 100644
--- a/tests/middleware_exceptions/urls.py
+++ b/tests/middleware_exceptions/urls.py
@@ -8,6 +8,7 @@ urlpatterns = [
url(r'^middleware_exceptions/error/$', views.server_error),
url(r'^middleware_exceptions/null_view/$', views.null_view),
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_error/$', views.template_response_error),
diff --git a/tests/middleware_exceptions/views.py b/tests/middleware_exceptions/views.py
index d3d7fae661..891c196728 100644
--- a/tests/middleware_exceptions/views.py
+++ b/tests/middleware_exceptions/views.py
@@ -32,3 +32,11 @@ def null_view(request):
def permission_denied(request):
raise PermissionDenied()
+
+
+def exception_in_render(request):
+ class CustomHttpResponse(http.HttpResponse):
+ def render(self):
+ raise Exception('Exception in HttpResponse.render()')
+
+ return CustomHttpResponse('Error')