Umleitung bei einer Ausnahme in Flask unter Verwendung einer decorator
Python Exception Handling decorators sind ein leistungsfähiges Mittel zur Reduzierung von Try-Except-Code.
In einer Flask -Anwendung implementieren Sie in der Regel globale Exception-Handler. In vielen Fällen ist dies ausreichend. Was aber, wenn Sie mehr Kontrolle wünschen?
In einem Projekt stellte ich eine Verbindung zu einem API her und wollte, dass eine Reihe von Routen, die den API verwenden, im Falle eines API -Fehlers auf eine 'Start'-Seite umleiten, natürlich mit einer entsprechenden Meldung. Ich habe dies mit einem 'redirect_decorator' Exception Handler implementiert, der auch einen Parameter hat, der den Endpunkt angibt. Der decorator wird verwendet, um zu vermeiden, dass jeder Route try-except-Code gegeben werden muss.
Projekt einrichten
Wie immer erstellen Sie ein virtual environment und Verzeichnisse, zum Beispiel durch Eingabe:
Translated with www.DeepL.com/Translator (free version)
python3 -m venv flask_except
source flask_except/bin/activate
cd flask_except
mkdir project
cd project
mkdir app
pip install flask
Im Projektverzeichnis befindet sich die Datei zum Ausführen der Anwendung:
# run.py
from app.factory import create_app
app = create_app()
if __name__ == '__main__':
app.run(
host='localhost',
debug=True,
)
Flask mit einem Error-Handler
Dies ist mehr oder weniger eine Kopie dessen, was auf der Website Flask zu finden ist.
# factory.py
from flask import Flask
def internal_server_error(e):
return 'An internal server error occurred', 500
def create_app():
app = Flask(__name__)
@app.route('/')
def welcome():
return 'Welcome'
@app.route('/error')
def error():
a = 2/0
return 'Error'
app.register_error_handler(500, internal_server_error)
return app
Im Projektverzeichnis starten wir die Anwendung:
python run.py
Dann kann man im Browser die Endpunkte überprüfen:
http://127.0.0.1:5000
http://127.0.0.1:5000/error
Wenn Sie die Meldung der Funktion internal_server_error() sehen wollen, setzen Sie debug=False in run.py. In diesem Fall wird der Server bei Änderungen nicht neu gestartet!
Fügen Sie eine API hinzu, die bei Fehlern Ausnahmen erzeugt
In Python erzeugen die meisten externen Pakete Ausnahmen, wenn Fehler auftreten. Genau das werden wir hier simulieren. Unser Paket API hat eine Funktion und erzeugt seine eigenen Ausnahmen.
# api: custom exception
class ServiceAPIError(Exception):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# api: function
def service_api_function():
if True:
raise ServiceAPIError(
reason='Connection problem',
description='Remote system not responding',
)
Wenn ein Fehler auftritt, löst die Funktion einen ServiceAPIError mit einem Grund und einer Beschreibung aus.
Die Weiterleitung decorator
Für maximale Flexibilität möchte ich in der Lage sein, einen Endpunkt in der decorator zu übergeben. Dies bedeutet, dass die decorator erneut umgeschlagen werden muss.
# our redirect decorator
def redirect_on_service_api_error(endpoint):
def decorator(f):
@functools.wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except ServiceAPIError as e:
return redirect(url_for(endpoint))
return func
return decorator
Hinzufügen der API und Umleitung der decorator
Unsere Anwendung hat eine oder mehrere Routen zur Ausführung von Aufgaben. Wenn eine Aufgabe aufgrund eines API -Fehlers fehlschlägt, möchten wir zur Route 'start' umgeleitet werden. Ich habe Druckanweisungen hinzugefügt, um alle in unserer decorator verfügbaren Informationen anzuzeigen:
# factory.py (with api, redirect decorator)
from flask import Flask, redirect, url_for
import functools
# api: custom exception
class ServiceAPIError(Exception):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# api: function
def service_api_function():
if True:
raise ServiceAPIError(
reason='Connection problem',
description='Remote system not responding',
)
# our redirect decorator
def redirect_on_service_api_error(endpoint):
def decorator(f):
@functools.wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except ServiceAPIError as e:
# debugging
print('Exception: {}, e = {}'.format(type(e).__name__, e))
print('- function = {}, args = {}, kwargs = {}'.format(f.__name__, args, kwargs))
print('- endpoint = {}'.format(endpoint))
print('- e.args = {}, e.kwargs = {}'.format(e.args, e.kwargs))
# redirect
return redirect(url_for(endpoint))
return func
return decorator
def internal_server_error(e):
return 'An internal server error occurred', 500
def create_app():
app = Flask(__name__)
@app.route('/')
def welcome():
return 'Welcome'
@app.route('/error')
def error():
a = 2/0
return 'Error'
@app.route('/start')
def start():
return 'Start'
@app.route('/tasks/<task>')
@redirect_on_service_api_error('start')
def tasks(task):
print('tasks: task = {}'.format(task))
service_api_function()
return 'Started task'
app.register_error_handler(500, internal_server_error)
return app
Rufen Sie nun Ihren Browser auf:
http://127.0.0.1:5000/tasks/mytask
und beobachten Sie, dass wir zu 'start' umgeleitet werden. Schön. In der Konsole wird das Folgende ausgegeben:
Exception: ServiceAPIError, e =
- function = tasks, args = (), kwargs = {'task': 'mytask'}
- endpoint = start
- e.args = (), e.kwargs = {'reason': 'Connection problem', 'description': 'Remote system not responding'}
Aber wir können es besser machen: Blinkende Meldung
Wenn ein API -Fehler auftritt und wir zu 'start' umgeleitet werden, wollen wir auch zeigen, was passiert ist. Dies können wir mit der Funktion flash() message in Flask erreichen. Vor der Weiterleitung in unserem decorator blinken() wir die Nachricht:
flash("There was a problem in '{}' with task '{}': {}".\
format(f.__name__, kwargs['task'], e.kwargs['reason']))
Ich werde dies nicht in Vorlagen implementieren, sondern stattdessen die Funktion get_flashed_messages() in der 'start' Route verwenden:
@app.route('/start')
def start():
return 'Start<br>' + ', '.join(get_flashed_messages())
Um flash() zu verwenden, müssen wir auch den secret_key zu unserer Anwendung hinzufügen.
Unsere endgültige Anwendung:
# factory.py (with api, redirect decorator, flashed messages)
from flask import Flask, redirect, url_for, flash, get_flashed_messages
import functools
# api: custom exception
class ServiceAPIError(Exception):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# api: function
def service_api_function():
if True:
raise ServiceAPIError(
reason='Connection problem',
description='Remote system not responding',
)
# our redirect decorator
def redirect_on_service_api_error(endpoint):
def decorator(f):
@functools.wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except ServiceAPIError as e:
# debugging
print('Exception: {}, e = {}'.format(type(e).__name__, e))
print('- function = {}, args = {}, kwargs = {}'.format(f.__name__, args, kwargs))
print('- endpoint = {}'.format(endpoint))
print('- e.args = {}, e.kwargs = {}'.format(e.args, e.kwargs))
# flash
flash("There was a problem in '{}' with task '{}': {}".\
format(f.__name__, kwargs['task'], e.kwargs['reason']))
# redirect
return redirect(url_for(endpoint))
return func
return decorator
def internal_server_error(e):
return 'An internal server error occurred', 500
def create_app():
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def welcome():
return 'Welcome'
@app.route('/error')
def error():
a = 2/0
return 'Error'
@app.route('/start')
def start():
return 'Start<br>' + ', '.join(get_flashed_messages())
@app.route('/tasks/<task>')
@redirect_on_service_api_error('start')
def tasks_task(task):
print('tasks, task = {}'.format(task))
service_api_function()
return 'Started task'
app.register_error_handler(500, internal_server_error)
return app
Rufen Sie nun Ihren Browser auf:
http://127.0.0.1:5000/tasks/mytask
und wir werden zu 'start' weitergeleitet, aber auch die Nachricht wird angezeigt. Jetzt wissen wir, was passiert ist!
Start
There was a problem in 'tasks_task' with task 'mytask': Connection problem
Zusammenfassung
Im Vergleich zu try-except haben wir in jeder Route viel weniger Code. Und in unserem Redirect decorator Exception Handler haben wir alle Informationen, die wir brauchen, um eine anständige Meldung anzuzeigen.
Links / Impressum
Flask - Handling Application Errors
https://flask.palletsprojects.com/en/2.1.x/errorhandling
Flask - Message Flashing
https://flask.palletsprojects.com/en/2.1.x/patterns/flashing
How do I pass extra arguments to a Python decorator?
https://stackoverflow.com/questions/10176226/how-do-i-pass-extra-arguments-to-a-python-decorator
Mehr erfahren
Decorator Exceptions Flask
Neueste
- Ausblenden der Primärschlüssel der Datenbank UUID Ihrer Webanwendung
- Don't Repeat Yourself (DRY) mit Jinja2
- SQLAlchemy, PostgreSQL, maximale Anzahl von Zeilen pro user
- Anzeige der Werte in den dynamischen Filtern SQLAlchemy
- Sichere Datenübertragung mit Public Key Verschlüsselung und pyNaCl
- rqlite: eine hochverfügbare und distverteilte SQLite -Alternative
Meistgesehen
- Verwendung von Pythons pyOpenSSL zur Überprüfung von SSL-Zertifikaten, die von einem Host heruntergeladen wurden
- Verwendung von UUIDs anstelle von Integer Autoincrement Primary Keys mit SQLAlchemy und MariaDb
- PyInstaller und Cython verwenden, um eine ausführbare Python-Datei zu erstellen
- Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
- SQLAlchemy: Verwendung von Cascade Deletes zum Löschen verwandter Objekte
- Flask RESTful API Validierung von Anfrageparametern mit Marshmallow-Schemas