How can I implement a custom error handler for all

2019-01-19 03:34发布

问题:

In my Flask app, I can easily expand the list of errors handled by a single custom error handler by adding errorhandler decorators for each error code as with

@application.errorhandler(404)
@application.errorhandler(401)
@application.errorhandler(500)
def http_error_handler(error):
    return flask.render_template('error.html', error=error), error.code

However this approach requires an explicit decorator for each error code. Is there a way decorate my (single) http_error_handler function so that it handles all HTTP errors?

回答1:

You're not the only one, one workaround will be specifying the list of the http error code you're catching and bound to application.error_handler_spec, and drop the decorators, like this:

def http_error_handler(error):
    return flask.render_template('error.html', error=error), error.code

for error in (401, 404, 500): # or with other http code you consider as error
    application.error_handler_spec[None][error] = http_error_handler

Not ideal and ugly I know, but it will work and I do hope someone else can come with a better solution. Hope this helps.



回答2:

You can use the errorhandler decorator with an exception class rather than an error code as an argument, as is described here. Thus you could try for instance

@application.errorhandler(HTTPException)
def http_error_handler(error):

to handle all HTTP errors (which presumably means all HTTP error codes), or even

@application.errorhandler(Exception)
def http_error_handler(error):

to handle all uncaught exceptions

Edit: Having looked at the flask source code, there is a 'TRAP_HTTP_EXCEPTIONS' flag in the app config, which you can change (by doing for instance app.config['TRAP_HTTP_EXCEPTIONS']=True).

(Roughly) When this flag is false, exceptions which are instances of HTTPException are handled by the functions you've decorated with errorhandler(n) where n is an HTTP error code; and when this flag is true, all instances of HTTPException are instead handled by the functions you've decorated with errorhandler(c), where c is an exception class.

Thus doing

app.config['TRAP_HTTP_EXCEPTIONS']=True

@application.errorhandler(Exception)
def http_error_handler(error):

should achieve what you want.

Since it looks like HTTPException has subclasses for each HTTP error code (see here), setting 'TRAP_HTTP_EXCEPTIONS' and decorating your error handlers with exception classes not error codes looks like a strictly more flexible way of doing things.

For reference, my flask error handling now looks like:

app.config['TRAP_HTTP_EXCEPTIONS']=True

@app.errorhandler(Exception)
def handle_error(e):
    try:
        if e.code < 400:
            return flask.Response.force_type(e, flask.request.environ)
        elif e.code == 404:
            return make_error_page("Page Not Found", "The page you're looking for was not found"), 404
        raise e
    except:
        return make_error_page("Error", "Something went wrong"), 500

This does everything I want, and seems to handle all errors, both HTTP and internal. The if e.code < 400 bit is there to use flask's default behaviour for redirects and the like (otherwise those end up as error 500s, which isn't what you want)



回答3:

For me, the following snippets was not working :

@app.errorhandler(HTTPException)
def _handle_http_exception(e):
    return make_response(render_template("errors/http_exception.html", code=e.code, description=e.description), e.code)

But changing HTTPException to the real one, like NotFound, was working. Don't ask me why, I didn't find the answer.

So I've found an alternative solution which works pretty well:

from werkzeug.exceptions import default_exceptions

def _handle_http_exception(e):
    return make_response(render_template("errors/http_exception.html", code=e.code, description=e.description), e.code)

for code in default_exceptions:
    app.errorhandler(code)(_handle_http_exception)

(Found at Github)