mirror of
https://github.com/django/django.git
synced 2025-10-24 22:26:08 +00:00
Fixed #24733 -- Passed the triggering exception to 40x error handlers
Thanks Tim Graham for the review.
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||
import logging
|
||||
import sys
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
@@ -13,6 +14,7 @@ from django.core.exceptions import (
|
||||
from django.db import connections, transaction
|
||||
from django.http.multipartparser import MultiPartParserError
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango21Warning
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.module_loading import import_string
|
||||
from django.views import debug
|
||||
@@ -80,10 +82,21 @@ class BaseHandler(object):
|
||||
view = transaction.atomic(using=db.alias)(view)
|
||||
return view
|
||||
|
||||
def get_exception_response(self, request, resolver, status_code):
|
||||
def get_exception_response(self, request, resolver, status_code, exception):
|
||||
try:
|
||||
callback, param_dict = resolver.resolve_error_handler(status_code)
|
||||
response = callback(request, **param_dict)
|
||||
# Unfortunately, inspect.getargspec result is not trustable enough
|
||||
# depending on the callback wrapping in decorators (frequent for handlers).
|
||||
# Falling back on try/except:
|
||||
try:
|
||||
response = callback(request, **dict(param_dict, exception=exception))
|
||||
except TypeError:
|
||||
warnings.warn(
|
||||
"Error handlers should accept an exception parameter. Update "
|
||||
"your code as this parameter will be required in Django 2.1",
|
||||
RemovedInDjango21Warning, stacklevel=2
|
||||
)
|
||||
response = callback(request, **param_dict)
|
||||
except:
|
||||
signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
|
||||
@@ -171,25 +184,25 @@ class BaseHandler(object):
|
||||
if settings.DEBUG:
|
||||
response = debug.technical_404_response(request, exc)
|
||||
else:
|
||||
response = self.get_exception_response(request, resolver, 404)
|
||||
response = self.get_exception_response(request, resolver, 404, exc)
|
||||
|
||||
except PermissionDenied:
|
||||
except PermissionDenied as exc:
|
||||
logger.warning(
|
||||
'Forbidden (Permission denied): %s', request.path,
|
||||
extra={
|
||||
'status_code': 403,
|
||||
'request': request
|
||||
})
|
||||
response = self.get_exception_response(request, resolver, 403)
|
||||
response = self.get_exception_response(request, resolver, 403, exc)
|
||||
|
||||
except MultiPartParserError:
|
||||
except MultiPartParserError as exc:
|
||||
logger.warning(
|
||||
'Bad request (Unable to parse request body): %s', request.path,
|
||||
extra={
|
||||
'status_code': 400,
|
||||
'request': request
|
||||
})
|
||||
response = self.get_exception_response(request, resolver, 400)
|
||||
response = self.get_exception_response(request, resolver, 400, exc)
|
||||
|
||||
except SuspiciousOperation as exc:
|
||||
# The request logger receives events for any problematic request
|
||||
@@ -205,7 +218,7 @@ class BaseHandler(object):
|
||||
if settings.DEBUG:
|
||||
return debug.technical_500_response(request, *sys.exc_info(), status_code=400)
|
||||
|
||||
response = self.get_exception_response(request, resolver, 400)
|
||||
response = self.get_exception_response(request, resolver, 400, exc)
|
||||
|
||||
except SystemExit:
|
||||
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from django import http
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, loader
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_text
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
|
||||
|
||||
@@ -7,7 +9,7 @@ from django.views.decorators.csrf import requires_csrf_token
|
||||
# therefore need @requires_csrf_token in case the template needs
|
||||
# {% csrf_token %}.
|
||||
@requires_csrf_token
|
||||
def page_not_found(request, template_name='404.html'):
|
||||
def page_not_found(request, exception, template_name='404.html'):
|
||||
"""
|
||||
Default 404 handler.
|
||||
|
||||
@@ -15,8 +17,24 @@ def page_not_found(request, template_name='404.html'):
|
||||
Context:
|
||||
request_path
|
||||
The path of the requested URL (e.g., '/app/pages/bad_page/')
|
||||
exception
|
||||
The message from the exception which triggered the 404 (if one was
|
||||
supplied), or the exception class name
|
||||
"""
|
||||
context = {'request_path': request.path}
|
||||
exception_repr = exception.__class__.__name__
|
||||
# Try to get an "interesting" exception message, if any (and not the ugly
|
||||
# Resolver404 dictionary)
|
||||
try:
|
||||
message = exception.args[0]
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(message, six.text_type):
|
||||
exception_repr = message
|
||||
context = {
|
||||
'request_path': request.path,
|
||||
'exception': exception_repr,
|
||||
}
|
||||
try:
|
||||
template = loader.get_template(template_name)
|
||||
body = template.render(context, request)
|
||||
@@ -46,7 +64,7 @@ def server_error(request, template_name='500.html'):
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def bad_request(request, template_name='400.html'):
|
||||
def bad_request(request, exception, template_name='400.html'):
|
||||
"""
|
||||
400 error handler.
|
||||
|
||||
@@ -57,6 +75,7 @@ def bad_request(request, template_name='400.html'):
|
||||
template = loader.get_template(template_name)
|
||||
except TemplateDoesNotExist:
|
||||
return http.HttpResponseBadRequest('<h1>Bad Request (400)</h1>', content_type='text/html')
|
||||
# No exception content is passed to the template, to not disclose any sensitive information.
|
||||
return http.HttpResponseBadRequest(template.render())
|
||||
|
||||
|
||||
@@ -64,7 +83,7 @@ def bad_request(request, template_name='400.html'):
|
||||
# therefore need @requires_csrf_token in case the template needs
|
||||
# {% csrf_token %}.
|
||||
@requires_csrf_token
|
||||
def permission_denied(request, template_name='403.html'):
|
||||
def permission_denied(request, exception, template_name='403.html'):
|
||||
"""
|
||||
Permission denied (403) handler.
|
||||
|
||||
@@ -78,4 +97,6 @@ def permission_denied(request, template_name='403.html'):
|
||||
template = loader.get_template(template_name)
|
||||
except TemplateDoesNotExist:
|
||||
return http.HttpResponseForbidden('<h1>403 Forbidden</h1>', content_type='text/html')
|
||||
return http.HttpResponseForbidden(template.render(request=request))
|
||||
return http.HttpResponseForbidden(
|
||||
template.render(request=request, context={'exception': force_text(exception)})
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user