Routeur
Qu'est-ce que le Routage
Router définit quel middleware et quel Handler traiteront une requête HTTP. C'est la fonctionnalité la plus fondamentale et centrale de Salvo.
En interne, un Router est essentiellement composé d'une série de filtres. Lorsqu'une requête arrive, le routeur se teste lui-même et ses descendants dans l'ordre, de haut en bas, pour voir s'ils peuvent correspondre à la requête. Si une correspondance réussit, les middlewares de toute la chaîne formée par le routeur et ses routeurs descendants sont exécutés séquentiellement. Si pendant le traitement, le statut de la Response est défini sur une erreur (4XX, 5XX) ou une redirection (3XX), les middlewares et Handler suivants seront ignorés. Vous pouvez également appeler manuellement ctrl.skip_rest() pour ignorer les middlewares et Handler restants.
Pendant le processus de correspondance, il existe un objet d'information de chemin d'URL, qui peut être considéré comme un objet qui doit être entièrement consommé par les filtres lors de la correspondance. Si tous les filtres d'un certain Router correspondent avec succès, et que cet objet d'information de chemin d'URL a été entièrement consommé, cela est considéré comme une "correspondance réussie".
Par exemple :
Est en fait équivalent à :
Si on accède à GET /articles/, c'est considéré comme une correspondance réussie, et list_articles est exécuté. Cependant, si on accède à GET /articles/123, la correspondance de route échoue et renvoie une erreur 404 car Router::with_path("articles") ne consomme que la partie /articles de l'information de chemin d'URL, laissant la partie /123 non consommée, donc la correspondance est considérée comme échouée. Pour réussir la correspondance, la route peut être modifiée en :
Ici, {**} correspond à n'importe quel chemin restant, donc il peut correspondre à GET /articles/123 et exécuter list_articles.
Définition Plate
Nous pouvons définir les routes dans un style plat :
Définition Arborescente
Nous pouvons également définir les routes dans une structure arborescente, ce qui est l'approche recommandée :
Cette forme de définition rend les définitions de Router hiérarchiques, claires et simples pour les projets complexes.
De nombreuses méthodes dans Router renvoient Self après avoir été appelées, facilitant l'écriture de code enchaîné. Parfois, vous devez décider comment router en fonction de certaines conditions. Le système de routage fournit également la fonction then, qui est facile à utiliser :
Cet exemple signifie que les routes pour créer, éditer et supprimer des articles ne sont ajoutées que lorsque le serveur est en admin_mode.
Récupération des Paramètres depuis les Routes
Dans le code ci-dessus, {id} définit un paramètre. Nous pouvons récupérer sa valeur via l'instance Request :
{id} correspond à un segment dans le chemin. Normalement, l'id d'un article n'est qu'un nombre. Dans ce cas, nous pouvons utiliser une expression régulière pour restreindre la règle de correspondance pour id, comme r"{id|\d+}".
Pour les types numériques, il existe une méthode encore plus simple en utilisant <id:num>. Les notations spécifiques sont :
{id:num}correspond à n'importe quel nombre de caractères chiffres.{id:num[10]}correspond exactement à un nombre spécifique de caractères chiffres ; ici, 10 signifie qu'il correspond exactement à 10 chiffres.{id:num(..10)}correspond à 1 à 9 caractères chiffres.{id:num(3..10)}correspond à 3 à 9 caractères chiffres.{id:num(..=10)}correspond à 1 à 10 caractères chiffres.{id:num(3..=10)}correspond à 3 à 10 caractères chiffres.{id:num(10..)}correspond à au moins 10 caractères chiffres.
Vous pouvez également correspondre à tous les segments de chemin restants en utilisant {**}, {*+}, ou {*?}. Pour une meilleure lisibilité du code, vous pouvez ajouter des noms appropriés pour rendre la sémantique du chemin plus claire, par exemple {**file_path}.
{**}: Représente une correspondance générique où la partie correspondante peut être une chaîne vide. Par exemple, le chemin/files/{**rest_path}correspond à/files,/files/abc.txt,/files/dir/abc.txt.{*+}: Représente une correspondance générique où la partie correspondante doit exister et ne peut pas être une chaîne vide. Par exemple, le chemin/files/{*+rest_path}ne correspond PAS à/filesmais correspond à/files/abc.txt,/files/dir/abc.txt.{*?}: Représente une correspondance générique où la partie correspondante peut être une chaîne vide mais ne peut contenir qu'un seul segment de chemin. Par exemple, le chemin/files/{*?rest_path}ne correspond PAS à/files/dir/abc.txtmais correspond à/files,/files/abc.txt.
Plusieurs expressions peuvent être combinées pour correspondre au même segment de chemin, par exemple /articles/article_{id:num}/, /images/{name}.{ext}.
Ajout de Middleware
Le middleware peut être ajouté via la fonction hoop sur un routeur :
Dans cet exemple, le routeur racine utilise check_authed pour vérifier si l'utilisateur actuel est connecté. Tous les routeurs descendants sont affectés par ce middleware.
Si les utilisateurs ne font que parcourir les informations des writer et les articles, nous pourrions préférer qu'ils puissent parcourir sans se connecter. Nous pouvons définir les routes comme ceci :
Même si deux routeurs ont la même définition de chemin path("articles"), ils peuvent toujours être ajoutés au même routeur parent.
Filtres
Router utilise en interne des filtres pour déterminer si une route correspond. Les filtres prennent en charge les opérations logiques de base en utilisant or ou and. Un routeur peut contenir plusieurs filtres ; lorsque tous les filtres correspondent avec succès, la route correspond avec succès.
L'information de chemin d'un site web est une structure arborescente, mais cette structure arborescente n'est pas équivalente à la structure arborescente organisant les routeurs. Un chemin de site web peut correspondre à plusieurs nœuds de routeur. Par exemple, certains contenus sous le chemin articles/ peuvent nécessiter une connexion pour être visualisés, tandis que d'autres non. Nous pouvons organiser les sous-chemins nécessitant une vérification de connexion sous un routeur contenant un middleware de vérification de connexion, et ceux ne nécessitant pas de vérification sous un autre routeur sans celui-ci :
Les routeurs utilisent des filtres pour filtrer les requêtes et les envoyer au middleware et au Handler correspondants pour traitement.
path et method sont deux des filtres les plus couramment utilisés. path est utilisé pour correspondre aux informations de chemin ; method est utilisé pour correspondre à la Méthode de la requête, par exemple GET, POST, PATCH, etc.
Nous pouvons connecter les filtres de routeur en utilisant and, or :
Filtre de Chemin
Les filtres basés sur les chemins de requête sont les plus fréquemment utilisés. Les filtres de chemin peuvent définir des paramètres, par exemple :
Dans un Handler, ils peuvent être récupérés via la fonction get_param de l'objet Request :
Filtre de Méthode
Filtre les requêtes en fonction de la Méthode de la requête HTTP, par exemple :
Ici, get, patch, delete sont tous des filtres de Méthode. C'est en fait équivalent à :
Wisp Personnalisé
Pour certaines expressions de correspondance fréquemment rencontrées, nous pouvons attribuer un nom court via PathFilter::register_wisp_regex ou PathFilter::register_wisp_builder. Par exemple, le format GUID apparaît souvent dans les chemins. La manière normale est de l'écrire ainsi à chaque fois qu'une correspondance est nécessaire :
Écrire cette expression régulière complexe à chaque fois est source d'erreurs et rend le code moins lisible. Vous pouvez plutôt faire ceci :
Vous n'avez besoin de l'enregistrer qu'une seule fois. Ensuite, vous pouvez directement utiliser une notation simple comme {id:guid} pour correspondre aux GUID, simplifiant l'écriture du code.
Venant d'un Framework Web basé sur Contrôleur, Comment Comprendre le Routeur ?
Les principales différences entre les frameworks web conçus avec routage (comme Salvo) et les frameworks traditionnels MVC ou conçus avec Contrôleur sont :
-
Flexibilité : La conception par routage permet une définition plus flexible des flux de traitement des requêtes, permettant un contrôle précis de la logique pour chaque chemin. Par exemple, dans Salvo, vous pouvez directement définir des fonctions de gestionnaire pour des chemins spécifiques :
Dans la conception par Contrôleur, vous devez généralement définir d'abord une classe contrôleur, puis définir plusieurs méthodes dans la classe pour gérer différentes requêtes :
-
Intégration du Middleware : Les frameworks de routage fournissent généralement une manière plus concise d'intégrer le middleware, permettant au middleware d'être appliqué à des routes spécifiques. Le middleware de Salvo peut être appliqué précisément à des routes particulières :
-
Organisation du Code : La conception par routage tend à organiser le code en fonction de la fonctionnalité ou des points de terminaison d'API, plutôt que la stratification Modèle-Vue-Contrôleur du MVC. La conception par routage encourage l'organisation du code selon la fonctionnalité des points de terminaison d'API :