1

I am using flask and I have my packages by feature and am using blue prints and this works nicely but I would like to have a global 404 and error page which sits outside of any specific feature package.

When I trigger a 404 flask handles this with the default 404 handler still and I dont get my custom template. Below is my code:

init.py

# Define the WSGI application object
app = Flask(__name__)

# Load Configuration
app.config.from_object('config')

from .user.route import mod_user as user_module
from .route.error_handler import mod_error as error_module

# Register blueprints
app.register_blueprint(user_module)
app.register_blueprint(error_module)

error_handler.py

import traceback

from flask import Flask, render_template, Blueprint

mod_error = Blueprint('error', __name__, template_folder='templates')


@mod_error.errorhandler(404)
def not_found(e):
    print(e)
    return render_template('404.html')


@mod_error.errorhandler(Exception)
def general_error(e):
    print(e)
    print(traceback.format_exc())
    return render_template('error.html')

My feature routes are defined in project.user.route.py

Global route\error handler is in project.route.error_handler.py

Global error templates are in project.templates

berimbolo
  • 3,319
  • 8
  • 43
  • 78

2 Answers2

1

I managed to work this out and it is pretty simple, when I moved from everything in one script to using blueprints and I created the error handling module I thought I needed to use the module name in my annotation:

@mod_error.errorhandler(404)

The reason for this is because this is how I did it in my controller for my user feature:

@mod_user.route('/read', methods=['POST', 'GET'])

Below is what I needed to do, which is import my app object and then use this for the error handler functions:

import traceback

from flask import Flask, render_template, Blueprint
from .. import app

mod_error = Blueprint('error', __name__, template_folder='templates')


@app.errorhandler(404)
def not_found(e):
    print(e)
    return render_template('404.html')


@app.errorhandler(Exception)
def general_exception(e):
    print(e)
    print(traceback.format_exc())
    return render_template('error.html')

This now handles all errors at a global level outside of any of my feature packages, I wanted to achieve the effect of a ControllerAdvice that handles all exceptions for all controllers in Spring MVC.

berimbolo
  • 3,319
  • 8
  • 43
  • 78
1

You actually don't have to import the app object like @berimbolo said. Yes, he does have the right idea, because in flask, for the 404 and other routing errors, the docs say:

the blueprint cannot handle 404 routing errors because the 404 occurs at the routing level before the blueprint can be determined.

So, you can't handle a 404 error using the errorhandler() on a blueprint. This won't work for 404 errors:

from flask import Flask, render_template, Blueprint
mod_error = Blueprint('error', __name__, template_folder='templates')

@mod_error.errorhandler(404)
def not_found(e):
    ...

Instead, you must add the error handler onto the application object, like @berimbolo did:

@app.errorhandler(404)
def not_found(e):
    print(e)
    return render_template('404.html')

But, the problem with this is you're increasing the change you'll hit a circular import issue (this sort of issue). Because the module where the app object is declared will likely import the module where the blueprint object is declared, at the same time as the blueprint module then imports the app module to get at the app object.

The solution: is to use the app_errorhandler method on the blueprint object. It's docstring says:

Like :meth:Flask.errorhandler but for a blueprint. This handler is used for all requests, even if outside of the blueprint.

This means you don't have to do the circular import thing. Instead, you can write:

import traceback
from flask import Flask, render_template, Blueprint

mod_error = Blueprint('error', __name__, template_folder='templates')

@mod_error.app_errorhandler(404)
def not_found(e):
    print(e)
    return render_template('404.html')

@app.app_errorhandler(Exception)
def general_exception(e):
    print(e)
    print(traceback.format_exc())
    return render_template('error.html')
Donal
  • 8,430
  • 3
  • 16
  • 21