Search code examples
pythonflaskblueprint

Blueprint 404 errorhandler doesn't activate under blueprint's url prefix


I created a blueprint with a 404 error handler. However, when I go to non-existent urls under the blueprint's prefix, the standard 404 page is shown rather than my custom one. How can I make the blueprint handle 404 errors correctly?

The following is a short app that demonstrates the problem. Navigating to http://localhost:5000/simple/asdf will not show the blueprint's error page.

#!/usr/local/bin/python
# coding: utf-8

from flask import *
from config import PORT, HOST, DEBUG

simplepage = Blueprint('simple', __name__, url_prefix='/simple')

@simplepage.route('/')
def simple_root():
    return 'This simple page'

@simplepage.errorhandler(404)
def error_simple(err):
    return 'This simple error 404', err

app = Flask(__name__)
app.config.from_pyfile('config.py')
app.register_blueprint(simplepage)

@app.route('/', methods=['GET'])
def api_get():    
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host=HOST,
            port=PORT,
            debug=DEBUG)

Solution

  • The documentation mentions that 404 error handlers will not behave as expected on blueprints. The app handles routing and raises a 404 before the request gets to the blueprint. The 404 handler will still activate for abort(404) because that is happening after routing at the blueprint level.

    This is something that could possibly be fixed in Flask (there's an open issue about it). As a workaround, you can do your own error routing within the top-level 404 handler.

    from flask import request, render_template
    
    @app.errorhandler(404)
    def handle_404(e):
        path = request.path
    
        # go through each blueprint to find the prefix that matches the path
        # can't use request.blueprint since the routing didn't match anything
        for bp_name, bp in app.blueprints.items():
            if path.startswith(bp.url_prefix):
                # get the 404 handler registered by the blueprint
                handler = app.error_handler_spec.get(bp_name, {}).get(404)
    
                if handler is not None:
                    # if a handler was found, return it's response
                    return handler(e)
    
        # return a default response
        return render_template('404.html'), 404