Dans l’univers du développement d'applications web, l'injection de dépendances et l’utilisation de middleware jouent un rôle crucial pour l'organisation et la gestion efficace des requêtes. FastAPI, un framework moderne et performant pour construire des APIs, offre une flexibilité impressionnante dans la gestion des dépendances et l'extension de la fonctionnalité de base, en particulier grâce à l’intégration facile de middleware personnalisé. Cela permet non seulement d’intercepter et de modifier les requêtes entrantes et les réponses sortantes, mais aussi de gérer des préoccupations transversales comme l'authentification, la journalisation ou le traitement des erreurs. L’utilisation de dépendances permet de simplifier la gestion des paramètres dans les endpoints, en particulier lorsque plusieurs paramètres sont nécessaires pour le bon fonctionnement d'une fonctionnalité.

Prenons l'exemple d'un endpoint qui nécessite plusieurs paramètres tels que time_range, category, et code. Une approche élégante consiste à regrouper tous ces paramètres dans une classe et à les utiliser comme dépendance dans cet endpoint. Cela permet non seulement de maintenir un code propre et bien organisé, mais aussi d’améliorer la lisibilité et la testabilité des différentes parties de l’application.

La création d’un middleware personnalisé, bien qu’optionnelle, est souvent essentielle pour la gestion de certaines fonctionnalités comme la journalisation des requêtes ou l’analyse de comportement des utilisateurs. Voici comment procéder pour développer un middleware qui interceptent les requêtes et les réponses dans une application FastAPI.

Création d’un middleware personnalisé dans FastAPI

Pour commencer, il est nécessaire de créer un module dédié pour notre middleware. Dans cet exemple, nous allons créer un fichier nommé middleware.py dans lequel nous allons définir notre classe ClientInfoMiddleware. Cette classe héritera de BaseHTTPMiddleware de la bibliothèque Starlette, qui est utilisée par FastAPI pour gérer les requêtes HTTP. Dans la méthode dispatch de cette classe, nous intercepterons les requêtes pour récupérer des informations comme l'adresse IP du client, le chemin demandé et la méthode HTTP utilisée. Cette information sera ensuite enregistrée à l'aide du logger par défaut de FastAPI.

L’exemple de code suivant montre comment implémenter ce middleware :

python
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware import logging logger = logging.getLogger("uvicorn.error") class ClientInfoMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): host_client = request.client.host requested_path = request.url.path method = request.method
logger.info(f"host client {host_client} requested {method} {requested_path} endpoint")
return await call_next(request)

Dans cet exemple, la classe ClientInfoMiddleware intercepte la requête, extrait l'adresse IP du client, le chemin demandé et la méthode HTTP, puis les enregistre dans les logs de l'application. Cette fonctionnalité est particulièrement utile pour des analyses de sécurité ou pour un suivi général des requêtes effectuées vers l’application.

Ajout du middleware à l’application FastAPI

Une fois le middleware défini, il est temps de l'intégrer dans l'application FastAPI. Cela se fait en ajoutant le middleware à l'application via la méthode add_middleware. Voici un exemple de code à ajouter dans le fichier main.py :

python
from fastapi import FastAPI
from app.middleware import ClientInfoMiddleware app = FastAPI() # Ajout du middleware app.add_middleware(ClientInfoMiddleware)

Avec cette configuration, chaque requête entrante sera interceptée par notre middleware, et des informations sur le client et la requête seront enregistrées dans les logs.

L’internationalisation et la localisation : concepts essentiels

L'internationalisation (i18n) et la localisation (l10n) sont des pratiques importantes dans le développement d'applications multilingues. L'internationalisation se réfère à la préparation d'une application pour qu'elle puisse être adaptée à différentes langues et cultures, tandis que la localisation consiste à adapter une application pour une région ou un marché spécifique, en tenant compte des spécificités telles que la langue, les unités de mesure ou les formats de date et de devise.

L'une des façons de gérer l'internationalisation et la localisation dans FastAPI est d'utiliser l’en-tête HTTP Accept-Language, qui permet à un serveur de connaître la préférence linguistique de l'utilisateur. Ce mécanisme est pris en charge par la plupart des navigateurs modernes et est un moyen simple et efficace de personnaliser l'expérience utilisateur en fonction de la langue.

Dans cet exemple, nous allons gérer deux langues : l'anglais américain (en_US) et le français de France (fr_FR). Nous allons utiliser la bibliothèque Babel pour gérer les traductions, et la dépendance Accept-Language pour choisir la langue la plus appropriée en fonction de la préférence de l'utilisateur.

Voici un exemple de code pour gérer l'internationalisation et la localisation dans une application FastAPI :

  1. Créez un fichier internationalization.py dans le répertoire app.

  2. Définissez les langues supportées et une fonction pour extraire la langue préférée du client.

python
from fastapi import Request from babel import Locale SUPPORTED_LOCALES = ['en_US', 'fr_FR'] def get_locale(request: Request): accept_language = request.headers.get('Accept-Language', 'en_US') locales = accept_language.split(',') for locale in locales: if locale in SUPPORTED_LOCALES: return locale return 'en_US' # Par défaut, on retourne l'anglais si aucune correspondance n'est trouvée

En appliquant cette méthode, nous pouvons offrir une expérience localisée à l'utilisateur en fonction de la langue préférée spécifiée dans l'en-tête de la requête. Ce processus permet à l’application de s’adapter aux besoins des utilisateurs sans nécessiter de multiples versions du même contenu.

Conclusion

L'intégration d'un middleware personnalisé et la gestion de l'internationalisation et de la localisation dans FastAPI peuvent transformer une application basique en une solution puissante et flexible. En regroupant les paramètres dans des classes et en utilisant l'injection de dépendances, il devient plus facile de maintenir un code propre et structuré. La possibilité de développer un middleware pour intercepter et analyser les requêtes avant de les traiter ajoute une couche supplémentaire de contrôle sur le fonctionnement de l'application. L’internationalisation permet, quant à elle, de personnaliser l'expérience utilisateur pour différents marchés et cultures, améliorant ainsi l'accessibilité et l'impact de l’application à l'échelle mondiale.

Comment implémenter des webhooks dans une application FastAPI ?

L'implémentation des webhooks dans une application FastAPI repose sur l'intégration d'une logique de middleware qui facilite l'envoi de notifications vers des points de terminaison externes lorsqu'un événement spécifique se produit. Pour ce faire, il est nécessaire de créer un middleware qui sera chargé de gérer la communication avec ces webhooks. Ce middleware, appelé WebhookSenderMiddleWare, est le cœur du processus de gestion des appels entrants via des webhooks.

Une fois le middleware défini, il est crucial de l'importer dans le fichier principal de l'application, généralement main.py. L'intégration se fait de la manière suivante :

python
from middleware.webhook import WebhookSenderMiddleWare # Reste du code
app.add_middleware(WebhookSenderMiddleWare)

Cela permet d'ajouter le middleware dans l'application, ce qui rend possible la gestion des callbacks des webhooks. À partir de ce moment-là, l'application est prête à traiter les événements envoyés par les webhooks, en appelant le serveur externe correspondant.

Documentation de l'API des Webhooks

Il est essentiel d'accompagner l'API de documentation détaillée afin que les utilisateurs comprennent parfaitement le fonctionnement du webhook. FastAPI permet d'intégrer cette documentation directement dans l'interface OpenAPI. Pour ce faire, vous devez créer une fonction dédiée au webhook avec un corps vide, puis déclarer ce point d'entrée comme un endpoint pour les webhooks. Voici un exemple de cette déclaration dans main.py :

python
@app.webhooks.post("/fastapi-webhook") def fastapi_webhook(event: Event): """_summary_ Args: event (Event): Événement reçu du webhook. Il contient des informations sur l'hôte, le chemin, le timestamp et le corps de la requête. """

De plus, vous pouvez enrichir cette documentation en ajoutant un exemple de contenu de corps en spécifiant des informations supplémentaires dans la classe Event, située dans le fichier middleware/webhook.py. Ce genre d'exemple aide les développeurs à mieux comprendre la structure des données qu'ils recevront via le webhook.

Test des Webhooks Locaux

Une fois le serveur lancé à l'aide de la commande uvicorn main:app, vous pouvez tester votre webhook en utilisant un serveur local. Par exemple, un serveur peut être mis en place sur le port 8080 à l'aide d'un script Python tel que http_server.py, téléchargeable depuis le répertoire GitHub. Une fois le serveur en fonctionnement, il est possible de tester l'envoi de requêtes à l'API en accédant à l'interface interactive située à l'adresse http://localhost:8000/docs.

Il suffit alors d'ajouter l'adresse localhost:8080 à l'aide de l'endpoint POST /register-webhook-url. Lorsque vous effectuez un appel vers un endpoint de l'API, l'application FastAPI enverra une notification au serveur local en écoutant sur le port 8080. Vous pouvez observer dans la console du serveur les événements qui sont transmis à chaque appel.

Concepts Avancés pour Améliorer les Webhooks

Bien que l'implémentation de base des webhooks soit déjà puissante, plusieurs concepts avancés peuvent rendre votre système de webhooks encore plus robuste, sécurisé et performant. Parmi les éléments les plus importants, on trouve :

  • Authentification : Il est primordial de sécuriser la communication entre l'API et le point de terminaison du webhook. Cela peut se faire par l'implémentation de mécanismes d'authentification tels que l'API key ou OAuth.

  • Mécanisme de Réessai : Les webhooks dépendent du protocole HTTP, qui peut rencontrer des échecs en raison de problèmes réseau ou d'indisponibilité temporaire du serveur. La mise en place d'un mécanisme de réessai permet de garantir que les événements seront livrés même si la première tentative échoue.

  • Stockage Persistant : Il est souvent utile de stocker les événements des webhooks dans une base de données afin de pouvoir auditer, résoudre des problèmes et rejouer les événements en cas de besoin. L’utilisation de bibliothèques comme SQLAlchemy peut faciliter cette intégration avec une base de données relationnelle.

  • Webhooks via WebSocket : Pour des mises à jour en temps réel, vous pouvez configurer un serveur WebSocket dans FastAPI. Cela permettra de notifier les clients via une connexion WebSocket dès qu’un webhook est reçu, rendant l’interaction avec l’application plus dynamique.

  • Limitation du Taux (Rate Limiting) : Pour éviter un abus de votre serveur et protéger contre une surcharge, il est conseillé d’appliquer une limitation de taux (rate limiting) à votre endpoint de webhook. Cela permet de réguler la fréquence des requêtes envoyées par un client, empêchant un seul client de saturer le serveur.

Conclusion

Les webhooks constituent un outil puissant pour créer des applications interactives et réactives, notamment en permettant une intégration fluide avec des systèmes tiers. Lorsqu'ils sont correctement implémentés et sécurisés, ils permettent à des applications FastAPI d’envoyer et de recevoir des notifications en temps réel, ouvrant la voie à des interactions dynamiques et sans faille entre services. Les techniques et pratiques mentionnées ci-dessus vous permettront d’optimiser et de sécuriser davantage vos webhooks, augmentant ainsi leur fiabilité et leur efficacité.

Comment exécuter une application FastAPI avec plusieurs travailleurs dans un conteneur Docker ?

Dans les environnements à fort trafic, faire tourner une application FastAPI avec un seul travailleur peut ne pas suffire pour traiter toutes les requêtes entrantes de manière efficace. Pour améliorer les performances et garantir une utilisation optimale des ressources, il est possible de répartir la charge entre plusieurs travailleurs. Cela peut être accompli à l’aide d’outils comme Gunicorn, qui permet de déployer une application FastAPI avec plusieurs processus de travail. Cette approche augmente la scalabilité et la réactivité du serveur, particulièrement dans des situations à fort volume de requêtes.

Préparation et configuration

Avant de commencer, il est important de noter que le paquet Gunicorn n'est pas compatible avec Windows. Pour garantir que l’application fonctionne correctement sur ce système d’exploitation, nous l'exécuterons à l’intérieur d’un conteneur Docker. Ce processus fait partie d’un projet précédent où nous avons exploré l’exécution d’applications FastAPI dans des conteneurs Docker.

Lorsque nous déployons FastAPI avec plusieurs travailleurs, chaque travailleur exécute une copie distincte de l'application, répartie sur différents processus CPU. Cela permet une gestion plus efficace des ressources et améliore la performance globale. Par exemple, dans le fichier main.py, il est possible d’ajouter les lignes suivantes pour visualiser l’ID du processus en cours d’exécution :

python
import logging
from os import getpid logger = logging.getLogger("uvicorn") @app.get("/") def read_root(): logger.info(f"Processd by worker {getpid()}") return {"Hello": "World"}

Cette modification permet de loguer l'ID du processus pour chaque requête, illustrant ainsi que l’application est gérée par plusieurs processus distincts, ce qui est essentiel pour mieux comprendre le comportement des travailleurs.

Utilisation de Gunicorn avec plusieurs travailleurs

Pour pouvoir utiliser Gunicorn, vous devez d'abord l’ajouter à votre fichier requirements.txt :

nginx
fastapi gunicorn

Ensuite, sur un système Linux ou macOS, vous pouvez installer Gunicorn via pip :

bash
$ pip install gunicorn

Une fois l’installation terminée, il est possible de démarrer le serveur avec quatre travailleurs en utilisant la commande suivante :

bash
$ gunicorn app.main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker

Si vous êtes sous Windows, comme mentionné plus tôt, l’utilisation de Docker est nécessaire. Dans ce cas, le fichier Dockerfile.dev doit être modifié pour inclure la commande suivante qui permettra de démarrer Gunicorn à la place de Uvicorn :

bash
CMD ["gunicorn", "app.main:app", "--bind", "0.0.0.0:80", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--log-level", "debug"]

Une fois ces modifications apportées, vous pouvez construire l'image Docker et exécuter le conteneur avec la commande suivante :

bash
$ docker build -t live-application-gunicorn -f Dockerfile.dev . $ docker run -p 8000:80 -i live-application-gunicorn

L’option -i permet d’exécuter le conteneur en mode interactif, ce qui est utile pour observer les logs générés lors de l’exécution. Après avoir lancé le serveur, vous pouvez accéder à l’interface de documentation interactive de FastAPI via http://localhost:8000/docs et faire des appels API pour vérifier le bon fonctionnement des travailleurs.

Limitations de Gunicorn et Uvicorn

Bien que Gunicorn soit un excellent outil pour exécuter une application FastAPI avec plusieurs travailleurs, il présente certains avantages par rapport à Uvicorn. Uvicorn peut également exécuter plusieurs travailleurs, mais son gestionnaire de processus est moins robuste que celui de Gunicorn. Gunicorn est donc généralement préféré lorsqu’il s’agit de gérer de nombreux processus avec des besoins élevés en termes de performance.

Cela dit, une des principales limitations de cette approche est que chaque travailleur fonctionne dans un espace mémoire distinct et ne peut pas partager de données entre les travailleurs. Cela signifie que toute composante de l’application qui doit conserver un état, comme les caches ou les sessions, doit être stockée dans un service centralisé ou distribué, comme Redis ou Memcached. De plus, l’augmentation du nombre de travailleurs entraîne un usage plus important des ressources, ce qui peut devenir problématique si l’application est gourmande en CPU ou en I/O.

Choisir le nombre optimal de travailleurs

Le nombre optimal de travailleurs n’est pas universel et doit être choisi en fonction des caractéristiques de l’application et des ressources disponibles. Une formule couramment utilisée pour déterminer ce nombre est la suivante : workers = (2 x cores) + 1, où cores fait référence au nombre de cœurs CPU disponibles sur le serveur. Cependant, il peut être nécessaire de procéder à des ajustements en fonction du type d’application et des tests effectués.

Considérations supplémentaires pour la gestion des applications FastAPI avec plusieurs travailleurs

Un autre point à considérer est que la mise en place de plusieurs travailleurs peut aussi entraîner une complexité accrue dans la gestion des logs et de l'état de l’application. La gestion des logs, par exemple, nécessite souvent l’utilisation d’un service de centralisation des logs pour éviter que les logs des différents processus ne se mélangent, ce qui compliquerait le débogage.

En résumé, bien que l’exécution d’une application FastAPI avec plusieurs travailleurs à l’aide de Gunicorn dans un conteneur Docker améliore la performance et la scalabilité, il est important de prendre en compte les coûts en ressources et de bien gérer l’état de l’application, notamment en utilisant des services externes comme Redis pour les sessions ou les caches partagés. L’expérimentation et l’ajustement continu des paramètres de déploiement sont souvent nécessaires pour atteindre des performances optimales.