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.
This commit is contained in:
Éloi Rivard 2024-12-11 23:19:11 +01:00
parent e031b74684
commit 309511e91c
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
2 changed files with 28 additions and 9 deletions

View file

@ -5,7 +5,9 @@ from urllib.parse import urlunsplit
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 model_converter(model):
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

View file

@ -40,15 +40,11 @@ if flask_themer:
@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 @@ else: # 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