Les Crash-Devs d'un Passionné

Mettre en place une gestion des exceptions centralisée

/Catégorie/python

Temps de lecture : 2 minutes

Je suis sûr que vous avez déjà rencontré ce genre de code dans votre controlleur

clipboard
Copier le code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@app.get("/orders/{id}")
async def get_order(id: int):
   try:
      # do something
   except MyException as e: 
      # manage this exception
      JSONResponse(
        status_code=404,
        content={"error": str(e)},
      )

Qu'est-ce qui ne va pas dans ce bout de code ?

Au moins la partie try/except

En effet, nous encapsulons notre code métier par ces instructions afin que le contrôleur fasse un retour correct à l'appelant (par exemple un client front attend une réponse sous format JSON)

Sauf que l'on se retrouve avec un bruit rendant difficile la lecture du code et une duplication potentielle de ce try/except dans d'autres routes

C'est là qu'intervient un Error Handler

C'est une fonctionnalité d'un framework comme Flask ou FastAPI. Je vais vous faire la démonstration dans ce dernier.

Un Error Handler est une couche dans le framework qui va capturer l'

exception
, comme le fait un except. Sauf qu'ici on le fait une seule fois et cela s'applique à toutes les routes de votre app.

On commence par créer notre fonction

json_response_error()
qui va gérer le format de retour

clipboard
Copier le code
1
2
3
4
5
def json_response_error(message: str, status_code: int):
    return JSONResponse(
        status_code=status_code,
        content={"error": message},
    )

J'utilise 2 fonctions qui seront exécutées par FastAPI. Une pour mon exception personnalisée

MyException
et une autre pour attraper les autres exceptions non gérées
Exception

clipboard
Copier le code
1
2
3
4
5
6
def my_error_exception_handler(request: Request, exception: MyException):
    return json_response_error(str(exception), status.HTTP_404_NOT_FOUND)


def exception_exception_handler(request: Request, exception: Exception):
    return json_response_error(str(exception), status.HTTP_500_INTERNAL_SERVER_ERROR)

Lors de l'initialisation du framework FastAPI, il faut préciser sur quel type d'exception il faut exécuter les fonctions vues au-dessus

clipboard
Copier le code
1
2
exception_handler = {MyException: my_error_exception_handler, Exception: exception_exception_handler}
app = FastAPI(exception_handlers=exception_handler)

Ce n'est pas plus compliqué que cela mais maintenant vous avez une application robuste et un code plus concis où vous pouvez vous focaliser sur le code métier

clipboard
Copier le code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Avant
@app.get("/orders/{id}")
async def get_order(id: int):
   try:
      # do something
   except MyException as e: 
      # manage this exception
      JSONResponse(
        status_code=404,
        content={"error": str(e)},
      )

# Après
@app.get("/orders/{id}")
async def get_order(id: int):
   # do something