forked from orbit-oss/flask
Added support for generic HTTPException handlers on app and blueprints
Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue #941, pr #1383, #2082, #2144
This commit is contained in:
parent
668061a5fc
commit
637eae5489
2 changed files with 69 additions and 44 deletions
33
flask/app.py
33
flask/app.py
|
|
@ -133,6 +133,8 @@ class Flask(_PackageBoundObject):
|
||||||
:param static_folder: the folder with static files that should be served
|
:param static_folder: the folder with static files that should be served
|
||||||
at `static_url_path`. Defaults to the ``'static'``
|
at `static_url_path`. Defaults to the ``'static'``
|
||||||
folder in the root path of the application.
|
folder in the root path of the application.
|
||||||
|
folder in the root path of the application. Defaults
|
||||||
|
to None.
|
||||||
:param host_matching: sets the app's ``url_map.host_matching`` to the given
|
:param host_matching: sets the app's ``url_map.host_matching`` to the given
|
||||||
given value. Defaults to False.
|
given value. Defaults to False.
|
||||||
:param static_host: the host to use when adding the static route. Defaults
|
:param static_host: the host to use when adding the static route. Defaults
|
||||||
|
|
@ -1460,15 +1462,17 @@ class Flask(_PackageBoundObject):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def _find_error_handler(self, e):
|
def _find_error_handler(self, e):
|
||||||
"""Finds a registered error handler for the request’s blueprint.
|
"""Find a registered error handler for a request in this order:
|
||||||
Otherwise falls back to the app, returns None if not a suitable
|
blueprint handler for a specific code, app handler for a specific code,
|
||||||
handler is found.
|
blueprint generic HTTPException handler, app generic HTTPException handler,
|
||||||
|
and returns None if a suitable handler is not found.
|
||||||
"""
|
"""
|
||||||
exc_class, code = self._get_exc_class_and_code(type(e))
|
exc_class, code = self._get_exc_class_and_code(type(e))
|
||||||
|
|
||||||
def find_handler(handler_map):
|
def find_handler(handler_map):
|
||||||
if not handler_map:
|
if not handler_map:
|
||||||
return
|
return
|
||||||
|
|
||||||
for cls in exc_class.__mro__:
|
for cls in exc_class.__mro__:
|
||||||
handler = handler_map.get(cls)
|
handler = handler_map.get(cls)
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
|
|
@ -1476,24 +1480,13 @@ class Flask(_PackageBoundObject):
|
||||||
handler_map[exc_class] = handler
|
handler_map[exc_class] = handler
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
# try blueprint handlers
|
# check for any in blueprint or app
|
||||||
handler = find_handler(self.error_handler_spec
|
for name, c in ((request.blueprint, code), (None, code),
|
||||||
.get(request.blueprint, {})
|
(request.blueprint, None), (None, None)):
|
||||||
.get(code))
|
handler = find_handler(self.error_handler_spec.get(name, {}).get(c))
|
||||||
if handler is not None:
|
|
||||||
return handler
|
|
||||||
|
|
||||||
# fall back to app handlers
|
if handler:
|
||||||
handler = find_handler(self.error_handler_spec[None].get(code))
|
return handler
|
||||||
if handler is not None:
|
|
||||||
return handler
|
|
||||||
|
|
||||||
try:
|
|
||||||
handler = find_handler(self.error_handler_spec[None][None])
|
|
||||||
except KeyError:
|
|
||||||
handler = None
|
|
||||||
|
|
||||||
return handler
|
|
||||||
|
|
||||||
def handle_http_exception(self, e):
|
def handle_http_exception(self, e):
|
||||||
"""Handles an HTTP exception. By default this will invoke the
|
"""Handles an HTTP exception. By default this will invoke the
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from werkzeug.exceptions import Forbidden, InternalServerError, HTTPException, NotFound
|
from werkzeug.exceptions import (
|
||||||
|
Forbidden,
|
||||||
|
InternalServerError,
|
||||||
|
HTTPException,
|
||||||
|
NotFound
|
||||||
|
)
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,29 +37,6 @@ def test_error_handler_no_match():
|
||||||
assert c.get('/keyerror').data == b'KeyError'
|
assert c.get('/keyerror').data == b'KeyError'
|
||||||
|
|
||||||
|
|
||||||
def test_default_error_handler():
|
|
||||||
app = flask.Flask(__name__)
|
|
||||||
|
|
||||||
@app.errorhandler(HTTPException)
|
|
||||||
def catchall_errorhandler(e):
|
|
||||||
assert isinstance(e, HTTPException)
|
|
||||||
assert isinstance(e, NotFound)
|
|
||||||
return 'default'
|
|
||||||
|
|
||||||
@app.errorhandler(Forbidden)
|
|
||||||
def catchall_errorhandler(e):
|
|
||||||
assert isinstance(e, Forbidden)
|
|
||||||
return 'forbidden'
|
|
||||||
|
|
||||||
@app.route('/forbidden')
|
|
||||||
def forbidden():
|
|
||||||
raise Forbidden()
|
|
||||||
|
|
||||||
c = app.test_client()
|
|
||||||
assert c.get('/undefined').data == b'default'
|
|
||||||
assert c.get('/forbidden').data == b'forbidden'
|
|
||||||
|
|
||||||
|
|
||||||
def test_error_handler_subclass():
|
def test_error_handler_subclass():
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
|
|
@ -161,3 +143,53 @@ def test_error_handler_blueprint():
|
||||||
|
|
||||||
assert c.get('/error').data == b'app-error'
|
assert c.get('/error').data == b'app-error'
|
||||||
assert c.get('/bp/error').data == b'bp-error'
|
assert c.get('/bp/error').data == b'bp-error'
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_error_handler():
|
||||||
|
bp = flask.Blueprint('bp', __name__)
|
||||||
|
|
||||||
|
@bp.errorhandler(HTTPException)
|
||||||
|
def bp_exception_handler(e):
|
||||||
|
assert isinstance(e, HTTPException)
|
||||||
|
assert isinstance(e, NotFound)
|
||||||
|
return 'bp-default'
|
||||||
|
|
||||||
|
@bp.errorhandler(Forbidden)
|
||||||
|
def bp_exception_handler(e):
|
||||||
|
assert isinstance(e, Forbidden)
|
||||||
|
return 'bp-forbidden'
|
||||||
|
|
||||||
|
@bp.route('/undefined')
|
||||||
|
def bp_registered_test():
|
||||||
|
raise NotFound()
|
||||||
|
|
||||||
|
@bp.route('/forbidden')
|
||||||
|
def bp_forbidden_test():
|
||||||
|
raise Forbidden()
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
|
@app.errorhandler(HTTPException)
|
||||||
|
def catchall_errorhandler(e):
|
||||||
|
assert isinstance(e, HTTPException)
|
||||||
|
assert isinstance(e, NotFound)
|
||||||
|
return 'default'
|
||||||
|
|
||||||
|
@app.errorhandler(Forbidden)
|
||||||
|
def catchall_errorhandler(e):
|
||||||
|
assert isinstance(e, Forbidden)
|
||||||
|
return 'forbidden'
|
||||||
|
|
||||||
|
@app.route('/forbidden')
|
||||||
|
def forbidden():
|
||||||
|
raise Forbidden()
|
||||||
|
|
||||||
|
app.register_blueprint(bp, url_prefix='/bp')
|
||||||
|
|
||||||
|
c = app.test_client()
|
||||||
|
assert c.get('/bp/undefined').data == b'bp-default'
|
||||||
|
assert c.get('/bp/forbidden').data == b'bp-forbidden'
|
||||||
|
assert c.get('/undefined').data == b'default'
|
||||||
|
assert c.get('/forbidden').data == b'forbidden'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue