Added HTTP exception trapping. This should fix #294
This commit is contained in:
parent
a070b4fe86
commit
7155f11a72
4 changed files with 86 additions and 8 deletions
2
CHANGES
2
CHANGES
|
|
@ -14,6 +14,8 @@ Relase date to be decided, codename to be chosen.
|
|||
- Empty session cookies are now deleted properly automatically.
|
||||
- View functions can now opt out of getting the automatic
|
||||
OPTIONS implementation.
|
||||
- HTTP exceptions and Bad Request Key Errors can now be trapped so that they
|
||||
show up normally in the traceback.
|
||||
|
||||
Version 0.7.3
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -81,6 +81,23 @@ The following configuration values are used internally by Flask:
|
|||
reject incoming requests with a
|
||||
content length greater than this by
|
||||
returning a 413 status code.
|
||||
``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will
|
||||
not execute the error handlers of HTTP
|
||||
exceptions but instead treat the
|
||||
exception like any other and bubble it
|
||||
through the exception stack. This is
|
||||
helpful for hairy debugging situations
|
||||
where you have to find out where an HTTP
|
||||
exception is coming from.
|
||||
``TRAP_BAD_REQUEST_KEY_ERRORS`` Werkzeug's internal data structures that
|
||||
deal with request specific data will
|
||||
raise special key errors that are also
|
||||
bad request exceptions. By default
|
||||
these will be converted into 400
|
||||
responses which however can make
|
||||
debugging some issues harder. If this
|
||||
config is set to ``True`` you will get
|
||||
a regular traceback instead.
|
||||
================================= =========================================
|
||||
|
||||
.. admonition:: More on ``SERVER_NAME``
|
||||
|
|
@ -114,6 +131,9 @@ The following configuration values are used internally by Flask:
|
|||
.. versionadded:: 0.7
|
||||
``PROPAGATE_EXCEPTIONS``, ``PRESERVE_CONTEXT_ON_EXCEPTION``
|
||||
|
||||
.. versionadded:: 0.8
|
||||
``TRAP_BAD_REQUEST_KEY_ERRORS``, ``TRAP_HTTP_EXCEPTIONS``
|
||||
|
||||
Configuring from Files
|
||||
----------------------
|
||||
|
||||
|
|
|
|||
36
flask/app.py
36
flask/app.py
|
|
@ -19,7 +19,7 @@ from itertools import chain
|
|||
from werkzeug.datastructures import ImmutableDict
|
||||
from werkzeug.routing import Map, Rule
|
||||
from werkzeug.exceptions import HTTPException, InternalServerError, \
|
||||
MethodNotAllowed
|
||||
MethodNotAllowed, BadRequest
|
||||
|
||||
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
|
||||
locked_cached_property, _tojson_filter, _endpoint_from_view_func
|
||||
|
|
@ -197,7 +197,9 @@ class Flask(_PackageBoundObject):
|
|||
'USE_X_SENDFILE': False,
|
||||
'LOGGER_NAME': None,
|
||||
'SERVER_NAME': None,
|
||||
'MAX_CONTENT_LENGTH': None
|
||||
'MAX_CONTENT_LENGTH': None,
|
||||
'TRAP_BAD_REQUEST_KEY_ERRORS': False,
|
||||
'TRAP_HTTP_EXCEPTIONS': False
|
||||
})
|
||||
|
||||
#: The rule object to use for URL rules created. This is used by
|
||||
|
|
@ -983,6 +985,24 @@ class Flask(_PackageBoundObject):
|
|||
return e
|
||||
return handler(e)
|
||||
|
||||
def trap_http_exception(self, e):
|
||||
"""Checks if an HTTP exception should be trapped or not. By default
|
||||
this will return `False` for all exceptions except for a bad request
|
||||
key error if ``TRAP_BAD_REQUEST_KEY_ERRORS`` is set to `True`. It
|
||||
also returns `True` if ``TRAP_HTTP_EXCEPTIONS`` is set to `True`.
|
||||
|
||||
This is called for all HTTP exceptions raised by a view function.
|
||||
If it returns `True` for any exception the error handler for this
|
||||
exception is not called and it shows up as regular exception in the
|
||||
traceback. This is helpful for debugging implicitly raised HTTP
|
||||
exceptions.
|
||||
"""
|
||||
if self.config['TRAP_HTTP_EXCEPTIONS']:
|
||||
return True
|
||||
if self.config['TRAP_BAD_REQUEST_KEY_ERRORS']:
|
||||
return isinstance(e, BadRequest) and isinstance(e, LookupError)
|
||||
return False
|
||||
|
||||
def handle_user_exception(self, e):
|
||||
"""This method is called whenever an exception occurs that should be
|
||||
handled. A special case are
|
||||
|
|
@ -993,14 +1013,16 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
# ensure not to trash sys.exc_info() at that point in case someone
|
||||
# wants the traceback preserved in handle_http_exception.
|
||||
if isinstance(e, HTTPException):
|
||||
return self.handle_http_exception(e)
|
||||
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
assert exc_value is e
|
||||
|
||||
# ensure not to trash sys.exc_info() at that point in case someone
|
||||
# wants the traceback preserved in handle_http_exception. Of course
|
||||
# we cannot prevent users from trashing it themselves in a custom
|
||||
# trap_http_exception method so that's their fault then.
|
||||
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
||||
return self.handle_http_exception(e)
|
||||
|
||||
blueprint_handlers = ()
|
||||
handlers = self.error_handler_spec.get(request.blueprint)
|
||||
if handlers is not None:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from contextlib import contextmanager
|
|||
from functools import update_wrapper
|
||||
from datetime import datetime
|
||||
from werkzeug import parse_date, parse_options_header
|
||||
from werkzeug.exceptions import NotFound
|
||||
from werkzeug.exceptions import NotFound, BadRequest
|
||||
from werkzeug.http import parse_set_header
|
||||
from jinja2 import TemplateNotFound
|
||||
from cStringIO import StringIO
|
||||
|
|
@ -592,6 +592,40 @@ class BasicFunctionalityTestCase(unittest.TestCase):
|
|||
c = app.test_client()
|
||||
assert c.get('/').data == '42'
|
||||
|
||||
def test_trapping_of_bad_request_key_errors(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.testing = True
|
||||
@app.route('/fail')
|
||||
def fail():
|
||||
flask.request.form['missing_key']
|
||||
c = app.test_client()
|
||||
assert c.get('/fail').status_code == 400
|
||||
|
||||
app.config['TRAP_BAD_REQUEST_KEY_ERRORS'] = True
|
||||
c = app.test_client()
|
||||
try:
|
||||
c.get('/fail')
|
||||
except KeyError, e:
|
||||
assert isinstance(e, BadRequest)
|
||||
else:
|
||||
self.fail('Expected exception')
|
||||
|
||||
def test_trapping_of_all_http_exceptions(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.testing = True
|
||||
app.config['TRAP_HTTP_EXCEPTIONS'] = True
|
||||
@app.route('/fail')
|
||||
def fail():
|
||||
flask.abort(404)
|
||||
|
||||
c = app.test_client()
|
||||
try:
|
||||
c.get('/fail')
|
||||
except NotFound, e:
|
||||
pass
|
||||
else:
|
||||
self.fail('Expected exception')
|
||||
|
||||
def test_teardown_on_pop(self):
|
||||
buffer = []
|
||||
app = flask.Flask(__name__)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue