Le Handler est un concept central du framework Salvo, que l'on peut simplement comprendre comme une unité de traitement des requêtes. Il a deux utilisations principales :
En tant que point de terminaison (Endpoint) : Un objet implémentant Handler
peut être placé dans le système de routage comme point final de traitement des requêtes. Lorsqu'on utilise la macro #[handler]
, une fonction peut directement servir de point de terminaison ; tandis qu'avec la macro #[endpoint]
, elle peut non seulement servir de point de terminaison, mais aussi générer automatiquement une documentation OpenAPI (ce point sera détaillé dans la documentation ultérieure).
En tant que middleware : Le même Handler
peut aussi servir de middleware, pour traiter la requête avant ou après qu'elle n'atteigne le point de terminaison final.
Le flux de traitement des requêtes dans Salvo peut être vu comme un "pipeline" : la requête passe d'abord par une série de middlewares (traitement vertical), puis atteint le point de terminaison correspondant (traitement horizontal). Qu'il s'agisse de middlewares ou de points de terminaison, tous sont des implémentations de Handler
, ce qui assure la cohérence et la flexibilité du système.
L'essence du modèle en oignon réside dans l'utilisation de ctrl.call_next()
avant et après le traitement, permettant ainsi un flux bidirectionnel de traitement des requêtes et réponses, où chaque middleware participe au cycle complet requête-réponse.
Un Handler est un objet responsable du traitement des requêtes Request. Le Handler est lui-même un Trait, contenant une méthode asynchrone handle
:
La fonction de traitement handle
a par défaut quatre paramètres, dans l'ordre : &mut Request, &mut Depot. &mut Response, &mut FlowCtrl
. Depot est un stockage temporaire pouvant contenir des données relatives à la requête en cours.
Selon son utilisation, il peut servir de middleware (hoop), permettant d'effectuer des traitements avant ou après que la requête n'atteigne le Handler
final - par exemple : vérification de connexion, compression de données, etc.
Les middlewares sont ajoutés via la fonction hoop
du Router
. Les middlewares ajoutés affectent le Router
courant et tous ses descendants.
Le Handler
peut aussi servir de Handler
final participant au routage, appelé goal
.
Handler
en tant que middleware (hoop)Lorsque le Handler
sert de middleware, il peut être ajouté à trois types d'objets supportant les middlewares :
Service
: Toute requête passera par les middlewares du Service
.
Router
: Seules les requêtes correspondant à une route passeront par tous les middlewares définis dans le Service
et ceux collectés le long du chemin correspondant.
Catcher
: Lorsqu'une erreur survient et qu'aucune information d'erreur personnalisée n'est écrite, la requête passera par les middlewares du Catcher
.
Handler
: Le Handler
lui-même supporte l'ajout de middlewares pour exécuter une logique pré ou post traitement.
#[handler]
#[handler]
simplifie considérablement l'écriture du code et améliore sa flexibilité.
Elle peut être appliquée à une fonction pour implémenter Handler
:
Ce qui équivaut à :
On constate qu'avec #[handler]
, le code devient beaucoup plus simple :
#[async_trait]
.Writer
ou Scribe
, ils peuvent être directement retournés comme valeur de fonction. Ici, &'static str
implémente Scribe
, donc peut être directement retourné.#[handler]
peut non seulement être appliqué à des fonctions, mais aussi aux blocs impl
de struct
, faisant implémenter Handler
à la struct
. Dans ce cas, la fonction handle
dans le bloc impl
sera reconnue comme l'implémentation concrète de handle
dans Handler
:
Dans Salvo, le Handler
peut retourner un Result
, à condition que les types Ok
et Err
de Result
implémentent le trait Writer
.
Considérant qu'anyhow est largement utilisé, lorsque la fonctionnalité anyhow
est activée, anyhow::Error
implémentera le trait Writer
. anyhow::Error
sera mappé à InternalServerError
.
Pour les types d'erreur personnalisés, vous pouvez afficher différentes pages d'erreur selon les besoins.