Skip to content

Commit

Permalink
Fixed #26915 -- Fixed regression handling responses returned from vie…
Browse files Browse the repository at this point in the history
…w middleware.
  • Loading branch information
AlexHill authored and timgraham committed Jul 25, 2016
1 parent ec4a6b3 commit 44a6b40
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 10 deletions.
13 changes: 7 additions & 6 deletions django/core/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,14 @@ def _get_response(self, request):
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:
Expand Down
9 changes: 8 additions & 1 deletion tests/middleware_exceptions/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.http import Http404, HttpResponse
from django.template import engines
from django.template.response import TemplateResponse

log = []

Expand Down Expand Up @@ -40,9 +41,15 @@ def process_view(self, request, view_func, view_args, view_kwargs):
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


Expand Down
37 changes: 37 additions & 0 deletions tests/middleware_exceptions/test_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,25 @@ def process_view(self, request, view_func, view_args, view_kwargs):
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)
Expand Down Expand Up @@ -512,6 +525,30 @@ def test_process_template_response_error(self):
# 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):

Expand Down
32 changes: 31 additions & 1 deletion tests/middleware_exceptions/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,40 @@ def test_process_view_return_response(self):
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):
Expand Down
4 changes: 2 additions & 2 deletions tests/middleware_exceptions/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 44a6b40

Please sign in to comment.