From 309511e91c13c04e0c39f0fe749969f7e436d11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 11 Dec 2024 23:19:11 +0100 Subject: [PATCH] feat: generic blueprint HTTP 404 error handler This allows blueprint to handle 404 errors. This is not supported directly in Flask. https://flask.palletsprojects.com/en/stable/errorhandling/#handling However, the blueprint cannot handle 404 routing errors because the 404 occurs at the routing level before the blueprint can be determined. --- canaille/app/flask.py | 18 ++++++++++++++++++ canaille/app/themes.py | 19 ++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/canaille/app/flask.py b/canaille/app/flask.py index 1c1e2a8e..32f99ee4 100644 --- a/canaille/app/flask.py +++ b/canaille/app/flask.py @@ -5,7 +5,9 @@ from flask import abort from flask import current_app +from flask import make_response from flask import request +from werkzeug.exceptions import HTTPException from werkzeug.routing import BaseConverter from canaille.app.i18n import gettext as _ @@ -107,3 +109,19 @@ def to_python(self, identifier): return instance return ModelConverter + + +def redirect_to_bp_handlers(app, error): + """Find and execute blueprint handlers matching an error. + + There is currently no way to make 404 handling generic: + https://flask.palletsprojects.com/en/stable/errorhandling/#handling + However, the blueprint cannot handle 404 routing errors because the + 404 occurs at the routing level before the blueprint can be determined. + """ + for bp in app.blueprints.values(): + if bp.url_prefix and request.path.startswith(bp.url_prefix): + for type_, handler in bp.error_handler_spec[None][None].items(): + if type_ in (error.code, HTTPException): # pragma: no branch + return make_response(handler(error)) + return None diff --git a/canaille/app/themes.py b/canaille/app/themes.py index 2230e8ff..f5139b1a 100644 --- a/canaille/app/themes.py +++ b/canaille/app/themes.py @@ -40,15 +40,11 @@ def unauthorized(error): @app.errorhandler(404) def page_not_found(error): - # There is currently no way to make 404 handling generic - # https://flask.palletsprojects.com/en/stable/errorhandling/#handling - # However, the blueprint cannot handle 404 routing errors because the - # 404 occurs at the routing level before the blueprint can be determined. - if flask.request.path.startswith("/scim/"): - from canaille.scim.endpoints import http_error_handler + from canaille.app.flask import redirect_to_bp_handlers - return http_error_handler(error) - return render_template("error.html", description=error, error_code=404), 404 + return redirect_to_bp_handlers(app, error) or render_template( + "error.html", description=error, error_code=404 + ), 404 @app.errorhandler(500) def server_error(error): # pragma: no cover @@ -58,4 +54,9 @@ def server_error(error): # pragma: no cover render_template = flask.render_template def setup_themer(app): - return + @app.errorhandler(404) + def page_not_found(error): + from canaille.app.flask import redirect_to_bp_handlers + + if not redirect_to_bp_handlers(app, error): + raise error