diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 55802f6a65..eddd576181 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -177,13 +177,14 @@ class BaseHandler(object): for middleware_method in self._view_middleware: response = middleware_method(request, callback, callback_args, callback_kwargs) if response: - return response + break - wrapped_callback = self.make_view_atomic(callback) - try: - response = wrapped_callback(request, *callback_args, **callback_kwargs) - except Exception as e: - response = self.process_exception_by_middleware(e, request) + if response is None: + wrapped_callback = self.make_view_atomic(callback) + try: + response = wrapped_callback(request, *callback_args, **callback_kwargs) + except Exception as e: + response = self.process_exception_by_middleware(e, request) # Complain if the view returned None (a common error). if response is None: diff --git a/tests/middleware_exceptions/middleware.py b/tests/middleware_exceptions/middleware.py index 690660fb53..b1de76a5ce 100644 --- a/tests/middleware_exceptions/middleware.py +++ b/tests/middleware_exceptions/middleware.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.http import Http404, HttpResponse from django.template import engines +from django.template.response import TemplateResponse log = [] @@ -40,9 +41,15 @@ class ProcessViewNoneMiddleware(BaseMiddleware): return None +class ProcessViewTemplateResponseMiddleware(BaseMiddleware): + def process_view(self, request, view_func, view_args, view_kwargs): + template = engines['django'].from_string('Processed view {{ view }}{% for m in mw %}\n{{ m }}{% endfor %}') + return TemplateResponse(request, template, {'mw': [self.__class__.__name__], 'view': view_func.__name__}) + + class TemplateResponseMiddleware(BaseMiddleware): def process_template_response(self, request, response): - response.template_name = engines['django'].from_string('template-response middleware') + response.context_data['mw'].append(self.__class__.__name__) return response diff --git a/tests/middleware_exceptions/test_legacy.py b/tests/middleware_exceptions/test_legacy.py index d59caa3882..227361c04a 100644 --- a/tests/middleware_exceptions/test_legacy.py +++ b/tests/middleware_exceptions/test_legacy.py @@ -56,12 +56,25 @@ class ViewMiddleware(TestMiddleware): return HttpResponse('View Middleware') +class TemplateResponseViewMiddleware(TestMiddleware): + def process_view(self, request, view_func, view_args, view_kwargs): + super(TemplateResponseViewMiddleware, self).process_view(request, view_func, view_args, view_kwargs) + template = engines['django'].from_string('TemplateResponse View Middleware') + return TemplateResponse(request, template) + + class ResponseMiddleware(TestMiddleware): def process_response(self, request, response): super(ResponseMiddleware, self).process_response(request, response) return HttpResponse('Response Middleware') +class ContentAccessingResponseMiddleware(TestMiddleware): + def process_response(self, request, response): + super(ContentAccessingResponseMiddleware, self).process_response(request, response) + return HttpResponse('Content-accessing Response Middleware: %d' % len(response.content)) + + class TemplateResponseMiddleware(TestMiddleware): def process_template_response(self, request, response): super(TemplateResponseMiddleware, self).process_template_response(request, response) @@ -512,6 +525,30 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): # Check that the right middleware methods have been invoked self.assert_middleware_usage(middleware, True, True, True, True, False) + def test_templateresponse_from_process_view_rendered(self): + view_middleware = TemplateResponseViewMiddleware() + # ContentAccessingResponseMiddleware tries to access response.content + # in its process_response(). + post_middleware = ContentAccessingResponseMiddleware() + self._add_middleware(view_middleware) + self._add_middleware(post_middleware) + self.assert_exceptions_handled('/middleware_exceptions/view/', []) + self.assert_middleware_usage(view_middleware, True, True, True, True, False) + self.assert_middleware_usage(post_middleware, True, True, True, True, False) + + def test_templateresponse_from_process_view_passed_to_template_response_middleware(self): + """ + TemplateResponses returned from process_view() should be passed to any + process_template_response(). + """ + view_middleware = TemplateResponseViewMiddleware() + resp_middleware = TemplateResponseMiddleware() + self._add_middleware(view_middleware) + self._add_middleware(resp_middleware) + self.assert_exceptions_handled('/middleware_exceptions/view/', []) + self.assert_middleware_usage(view_middleware, True, True, True, True, False) + self.assert_middleware_usage(resp_middleware, True, True, True, True, False) + class BadMiddlewareTests(BaseMiddlewareExceptionTest): diff --git a/tests/middleware_exceptions/tests.py b/tests/middleware_exceptions/tests.py index f6a7e24e59..230d062c88 100644 --- a/tests/middleware_exceptions/tests.py +++ b/tests/middleware_exceptions/tests.py @@ -22,10 +22,40 @@ class MiddlewareTests(SimpleTestCase): response = self.client.get('/middleware_exceptions/view/') self.assertEqual(response.content, b'Processed view normal_view') + @override_settings(MIDDLEWARE=[ + 'middleware_exceptions.middleware.ProcessViewTemplateResponseMiddleware', + 'middleware_exceptions.middleware.LogMiddleware', + ]) + def test_templateresponse_from_process_view_rendered(self): + """ + TemplateResponses returned from process_view() must be rendered before + being passed to any middleware that tries to access response.content, + such as middleware_exceptions.middleware.LogMiddleware. + """ + response = self.client.get('/middleware_exceptions/view/') + self.assertEqual(response.content, b'Processed view normal_view\nProcessViewTemplateResponseMiddleware') + + @override_settings(MIDDLEWARE=[ + 'middleware_exceptions.middleware.ProcessViewTemplateResponseMiddleware', + 'middleware_exceptions.middleware.TemplateResponseMiddleware', + ]) + def test_templateresponse_from_process_view_passed_to_process_template_response(self): + """ + TemplateResponses returned from process_view() should be passed to any + template response middleware. + """ + response = self.client.get('/middleware_exceptions/view/') + expected_lines = [ + b'Processed view normal_view', + b'ProcessViewTemplateResponseMiddleware', + b'TemplateResponseMiddleware', + ] + self.assertEqual(response.content, b'\n'.join(expected_lines)) + @override_settings(MIDDLEWARE=['middleware_exceptions.middleware.TemplateResponseMiddleware']) def test_process_template_response(self): response = self.client.get('/middleware_exceptions/template_response/') - self.assertEqual(response.content, b'template-response middleware') + self.assertEqual(response.content, b'template_response OK\nTemplateResponseMiddleware') @override_settings(MIDDLEWARE=['middleware_exceptions.middleware.LogMiddleware']) def test_view_exception_converted_before_middleware(self): diff --git a/tests/middleware_exceptions/views.py b/tests/middleware_exceptions/views.py index 891c196728..f006147daf 100644 --- a/tests/middleware_exceptions/views.py +++ b/tests/middleware_exceptions/views.py @@ -9,8 +9,8 @@ def normal_view(request): def template_response(request): - template = engines['django'].from_string('OK') - return TemplateResponse(request, template) + template = engines['django'].from_string('template_response OK{% for m in mw %}\n{{ m }}{% endfor %}') + return TemplateResponse(request, template, context={'mw': []}) def template_response_error(request):