diff --git a/django/views/defaults.py b/django/views/defaults.py index 1176bdeeeb..2fb0d0b38c 100644 --- a/django/views/defaults.py +++ b/django/views/defaults.py @@ -11,6 +11,17 @@ ERROR_404_TEMPLATE_NAME = '404.html' ERROR_403_TEMPLATE_NAME = '403.html' ERROR_400_TEMPLATE_NAME = '400.html' ERROR_500_TEMPLATE_NAME = '500.html' +ERROR_PAGE_TEMPLATE = """ + + + + %(title)s + + +

%(title)s

%(details)s

+ + +""" # This can be called when CsrfViewMiddleware.process_view has not run, @@ -55,8 +66,11 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME): # Render template (even though there are no substitutions) to allow # inspecting the context in tests. template = Engine().from_string( - '

Not Found

' - '

The requested resource was not found on this server.

') + ERROR_PAGE_TEMPLATE % { + 'title': 'Not Found', + 'details': 'The requested resource was not found on this server.', + }, + ) body = template.render(Context(context)) content_type = 'text/html' return HttpResponseNotFound(body, content_type=content_type) @@ -76,7 +90,10 @@ def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): if template_name != ERROR_500_TEMPLATE_NAME: # Reraise if it's a missing custom template. raise - return HttpResponseServerError('

Server Error (500)

', content_type='text/html') + return HttpResponseServerError( + ERROR_PAGE_TEMPLATE % {'title': 'Server Error (500)', 'details': ''}, + content_type='text/html', + ) return HttpResponseServerError(template.render()) @@ -94,7 +111,10 @@ def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME): if template_name != ERROR_400_TEMPLATE_NAME: # Reraise if it's a missing custom template. raise - return HttpResponseBadRequest('

Bad Request (400)

', content_type='text/html') + return HttpResponseBadRequest( + ERROR_PAGE_TEMPLATE % {'title': 'Bad Request (400)', 'details': ''}, + content_type='text/html', + ) # No exception content is passed to the template, to not disclose any sensitive information. return HttpResponseBadRequest(template.render()) @@ -119,7 +139,10 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME) if template_name != ERROR_403_TEMPLATE_NAME: # Reraise if it's a missing custom template. raise - return HttpResponseForbidden('

403 Forbidden

', content_type='text/html') + return HttpResponseForbidden( + ERROR_PAGE_TEMPLATE % {'title': '403 Forbidden', 'details': ''}, + content_type='text/html', + ) return HttpResponseForbidden( template.render(request=request, context={'exception': str(exception)}) ) diff --git a/tests/view_tests/tests/test_defaults.py b/tests/view_tests/tests/test_defaults.py index 4a7ad2f7bd..7ddb698422 100644 --- a/tests/view_tests/tests/test_defaults.py +++ b/tests/view_tests/tests/test_defaults.py @@ -81,8 +81,7 @@ class DefaultsTests(TestCase): def test_bad_request(self): request = self.request_factory.get('/') response = bad_request(request, Exception()) - self.assertEqual(response.status_code, 400) - self.assertEqual(response.content, b'

Bad Request (400)

') + self.assertContains(response, b'

Bad Request (400)

', status_code=400) @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -134,3 +133,18 @@ class DefaultsTests(TestCase): with self.assertRaises(TemplateDoesNotExist): server_error(request, template_name='nonexistent') + + def test_error_pages(self): + request = self.request_factory.get('/') + for response, title in ( + (bad_request(request, Exception()), b'Bad Request (400)'), + (permission_denied(request, Exception()), b'403 Forbidden'), + (page_not_found(request, Http404()), b'Not Found'), + (server_error(request), b'Server Error (500)'), + ): + with self.subTest(title=title): + self.assertIn(b'', response.content) + self.assertIn(b'', response.content) + self.assertIn(b'', response.content) + self.assertIn(b'%s' % title, response.content) + self.assertIn(b'', response.content)