Router
définit quels middlewares et Handler
traiteront une requête HTTP. C'est la fonctionnalité la plus basique et centrale de Salvo.
En interne, un Router
est en réalité composé d'une série de filtres (Filter). Lorsqu'une requête arrive, le routeur teste successivement de haut en bas, dans l'ordre d'ajout, si lui-même et ses descendants peuvent correspondre à la requête. Si une correspondance est trouvée, les middlewares de toute la chaîne formée par le routeur et ses descendants sont exécutés séquentiellement. Si pendant le traitement, l'état de la Response
est défini comme une erreur (4XX, 5XX) ou une redirection (3XX), les middlewares et Handler
suivants sont ignorés. Vous pouvez aussi appeler manuellement ctrl.skip_rest()
pour ignorer les middlewares et Handler
suivants.
Pendant le processus de correspondance, il existe une information de chemin URL qui peut être considérée comme un objet devant être entièrement consommé par le Filter lors de la correspondance. Si tous les Filtres d'un Router correspondent avec succès et que cette information de chemin URL a été entièrement consommée, alors on considère qu'il y a "correspondance réussie".
Par exemple :
Est en réalité équivalent à :
Si on accède à GET /articles/
, cela sera considéré comme une correspondance réussie, et list_articles
sera exécuté. Mais si on accède à GET /articles/123
, la correspondance échouera et une erreur 404 sera retournée, car Router::with_path("articles")
n'a consommé que /articles
dans l'information de chemin URL, laissant /123
non consommé, donc la correspondance échoue. Pour permettre la correspondance, le routeur peut être modifié ainsi :
Ici, {**}
correspondra à n'importe quel chemin supplémentaire, donc il pourra correspondre à GET /articles/123
et exécuter list_articles
.
Nous pouvons définir les routes dans un style plat :
Nous pouvons aussi définir les routes de manière arborescente, ce qui est la méthode recommandée :
Cette forme de définition permet pour les projets complexes de garder la définition des Router claire et simple.
Dans Router
, de nombreuses méthodes retournent l'instance elle-même (Self), permettant un enchaînement fluide du code. Parfois, vous devez décider comment router en fonction de certaines conditions. Le système de routage fournit aussi la fonction then
, très simple à utiliser :
Cet exemple montre que les routes pour créer, éditer ou supprimer des articles ne seront ajoutées que si le serveur est en mode admin_mode
.
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 du chemin. Normalement, l'id
d'un article est juste un nombre, donc nous pouvons utiliser une expression régulière pour limiter les règles de correspondance de id
à r"{id|\d+}"
.
Pour ce type numérique, une méthode plus simple est d'utiliser <id:num>
, avec les syntaxes suivantes :
{id:num}
, correspond à n'importe quel nombre de chiffres ;{id:num[10]}
, correspond exactement à un nombre spécifique de chiffres, ici 10 ;{id:num(..10)}
, correspond à 1 à 9 chiffres ;{id:num(3..10)}
, correspond à 3 à 9 chiffres ;{id:num(..=10)}
, correspond à 1 à 10 chiffres ;{id:num(3..=10)}
, correspond à 3 à 10 chiffres ;{id:num(10..)}
, correspond à au moins 10 chiffres.Il est aussi possible d'utiliser {**}
, {*+}
ou {*?}
pour correspondre à tous les segments de chemin restants. Pour une meilleure lisibilité, vous pouvez aussi ajouter un nom approprié pour clarifier la sémantique du chemin, comme {**file_path}
.
{**}
: correspond à une partie pouvant être une chaîne vide, par exemple le chemin /files/{**rest_path}
correspondra à /files
, /files/abc.txt
, /files/dir/abc.txt
;{*+}
: la partie correspondante doit exister et ne peut pas être une chaîne vide, par exemple /files/{*+rest_path}
ne correspondra pas à /files
mais correspondra à /files/abc.txt
, /files/dir/abc.txt
;{*?}
: la partie correspondante peut être une chaîne vide, mais ne peut contenir qu'un seul segment de chemin, par exemple /files/{*?rest_path}
ne correspondra pas à /files/dir/abc.txt
mais correspondra à /files
, /files/abc.txt
.Il est possible de combiner plusieurs expressions pour correspondre au même segment de chemin, comme /articles/article_{id:num}/
, /images/{name}.{ext}
.
Vous pouvez ajouter des middlewares via la fonction hoop
du routeur :
Dans cet exemple, le routeur racine utilise check_authed
pour vérifier si l'utilisateur actuel est connecté. Tous les routeurs descendants seront affectés par ce middleware.
Si les utilisateurs peuvent seulement consulter les informations des writers
et leurs articles sans avoir besoin de se connecter, nous pouvons définir les routes comme ceci :
Bien que deux routes aient la même définition de chemin path("articles")
, elles peuvent toujours être ajoutées au même routeur parent.
Un Router
détermine s'il correspond via des filtres internes. Les filtres supportent les opérations logiques de base or
et and
. Un routeur peut contenir plusieurs filtres, et ne correspondra que si tous les filtres correspondent avec succès.
La structure des chemins d'un site est arborescente, mais cette structure ne correspond pas nécessairement à l'organisation arborescente des routes. Un chemin de site peut correspondre à plusieurs nœuds de route. Par exemple, certains contenus sous le chemin articles/
nécessitent une connexion pour être consultés, alors que d'autres non. Nous pouvons organiser les sous-chemins nécessitant une authentification sous un routeur contenant le middleware de vérification, et les autres sous un routeur sans cette vérification :
Les routeurs utilisent des filtres pour filtrer les requêtes et les envoyer aux middlewares et Handler
correspondants.
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, comme GET, POST, PATCH, etc.
Nous pouvons utiliser and
, or
pour connecter les filtres d'un routeur :
Les filtres basés sur le chemin de la requête sont les plus fréquemment utilisés. Les filtres de chemin peuvent définir des paramètres, comme :
Dans un Handler
, les paramètres peuvent être récupérés via la fonction get_param
de l'objet Request
:
Filtre les requêtes selon la méthode HTTP
, comme :
Ici, get
, patch
, delete
sont des filtres de méthode. C'est équivalent à :
Pour certaines expressions de correspondance fréquemment utilisées, nous pouvons leur donner un nom court via PathFilter::register_wisp_regex
ou PathFilter::register_wisp_builder
. Par exemple, le format GUID apparaît souvent dans les chemins. Normalement, nous devrions écrire à chaque fois :
Écrire cette expression régulière complexe à chaque fois est sujet aux erreurs et peu esthétique. Nous pouvons plutôt faire :
Il suffit de l'enregistrer une fois, et ensuite nous pouvons utiliser simplement {id:guid}
pour correspondre à un GUID, simplifiant grandement l'écriture du code.
Les principales différences entre les frameworks web basés sur le routage (comme Salvo) et les frameworks traditionnels de type MVC ou Controller sont :
Flexibilité : La conception par routage permet une définition plus flexible du flux de traitement des requêtes, avec un contrôle précis de la logique de traitement pour chaque chemin. Par exemple, dans Salvo vous pouvez directement définir la fonction de traitement pour un chemin spécifique :
Alors qu'avec une conception Controller, vous devez généralement définir une classe contrôleur avec plusieurs méthodes pour gérer différentes requêtes :
Intégration des middlewares : Les frameworks de routage offrent généralement des moyens plus simples d'intégrer des middlewares, pouvant être appliqués à des routes spécifiques. Les middlewares de Salvo peuvent être appliqués précisément :
Organisation du code : Le routage favorise une organisation basée sur les fonctionnalités ou les points d'API plutôt que sur la séparation modèle-vue-contrôleur du MVC. Le routage encourage à organiser le code par fonctionnalité des points d'API :
Légèreté : Généralement, le routage est plus léger, avec moins de concepts et contraintes imposés par le framework. Vous pouvez n'inclure que les composants nécessaires sans suivre une structure stricte.
Le routage rend le développement d'API plus intuitif, particulièrement adapté pour les microservices modernes et les API RESTful. Dans des frameworks comme Salvo, le routage est un concept central qui reflète directement la structure et le comportement de l'API, rendant le code plus facile à comprendre et maintenir. En comparaison, les conceptions Controller traditionnelles nécessitent souvent plus de configuration et conventions pour réaliser les mêmes fonctionnalités.
Catégorie | Méthode | Description |
---|---|---|
**Création/Acc |