Route Guards
Introduction
Les route guards fournissent un middleware de navigation dans Nylo Website. Ils interceptent les transitions de route et vous permettent de controler si un utilisateur peut acceder a une page, de le rediriger ailleurs ou de modifier les donnees transmises a une route.
Les cas d'utilisation courants incluent :
- Verification d'authentification -- rediriger les utilisateurs non authentifies vers une page de connexion
- Acces base sur les roles -- restreindre les pages aux utilisateurs administrateurs
- Validation des donnees -- s'assurer que les donnees requises existent avant la navigation
- Enrichissement des donnees -- attacher des donnees supplementaires a une route
Les guards sont executes dans l'ordre avant que la navigation ne se produise. Si un guard retourne handled, la navigation s'arrete (soit par redirection, soit par abandon).
Creer un Route Guard
Creez un route guard en utilisant le CLI Metro :
metro make:route_guard auth
Cela genere un fichier guard :
import 'package:nylo_framework/nylo_framework.dart';
class AuthRouteGuard extends NyRouteGuard {
AuthRouteGuard();
@override
Future<GuardResult> onBefore(RouteContext context) async {
// Add your guard logic here
return next();
}
}
Cycle de vie du Guard
Chaque route guard possede trois methodes de cycle de vie :
onBefore
Appelee avant que la navigation ne se produise. C'est ici que vous verifiez les conditions et decidez d'autoriser, de rediriger ou d'abandonner la navigation.
@override
Future<GuardResult> onBefore(RouteContext context) async {
bool isLoggedIn = await Auth.isAuthenticated();
if (!isLoggedIn) {
return redirect(HomePage.path);
}
return next();
}
Valeurs de retour :
next()-- continuer vers le guard suivant ou naviguer vers la routeredirect(path)-- rediriger vers une route differenteabort()-- annuler completement la navigation
onAfter
Appelee apres une navigation reussie. Utilisez-la pour les analyses, la journalisation ou les effets secondaires post-navigation.
@override
Future<void> onAfter(RouteContext context) async {
// Log page view
Analytics.trackPageView(context.routeName);
}
onLeave
Appelee lorsque l'utilisateur quitte une route. Retournez false pour empecher l'utilisateur de partir.
@override
Future<bool> onLeave(RouteContext context) async {
if (hasUnsavedChanges) {
// Show confirmation dialog
return await showConfirmDialog();
}
return true; // Allow leaving
}
RouteContext
L'objet RouteContext est transmis a toutes les methodes de cycle de vie du guard et contient des informations sur la navigation :
| Propriete | Type | Description |
|---|---|---|
context |
BuildContext? |
Contexte de construction actuel |
data |
dynamic |
Donnees transmises a la route |
queryParameters |
Map<String, String> |
Parametres de requete de l'URL |
routeName |
String |
Nom/chemin de la route cible |
originalRouteName |
String? |
Nom de route original avant transformations |
@override
Future<GuardResult> onBefore(RouteContext context) async {
// Access route information
String route = context.routeName;
dynamic routeData = context.data;
Map<String, String> params = context.queryParameters;
return next();
}
Transformer le RouteContext
Creez une copie avec des donnees differentes :
// Change the data type
RouteContext<User> userContext = context.withData<User>(currentUser);
// Copy with modified fields
RouteContext updated = context.copyWith(
data: enrichedData,
queryParameters: {"tab": "settings"},
);
Actions du Guard
next
Continuer vers le guard suivant dans la chaine, ou naviguer vers la route si c'est le dernier guard :
return next();
redirect
Rediriger l'utilisateur vers une route differente :
return redirect(LoginPage.path);
Avec des options supplementaires :
return redirect(
LoginPage.path,
data: {"returnTo": context.routeName},
navigationType: NavigationType.pushReplace,
queryParameters: {"source": "guard"},
);
| Parametre | Type | Defaut | Description |
|---|---|---|---|
path |
Object |
requis | Chemin de route ou RouteView |
data |
dynamic |
null | Donnees a transmettre a la route de redirection |
queryParameters |
Map<String, dynamic>? |
null | Parametres de requete |
navigationType |
NavigationType |
pushReplace |
Methode de navigation |
result |
dynamic |
null | Resultat a retourner |
removeUntilPredicate |
Function? |
null | Predicat de suppression de route |
transitionType |
TransitionType? |
null | Type de transition de page |
onPop |
Function(dynamic)? |
null | Callback au retour |
abort
Annuler la navigation sans redirection. L'utilisateur reste sur sa page actuelle :
return abort();
setData
Modifier les donnees qui seront transmises aux guards suivants et a la route cible :
@override
Future<GuardResult> onBefore(RouteContext context) async {
User user = await fetchUser();
// Enrich the route data
setData({"user": user, "originalData": context.data});
return next();
}
Appliquer des Guards aux routes
Ajoutez des guards a des routes individuelles dans votre fichier de routeur :
appRouter() => nyRoutes((router) {
router.route(
HomePage.path,
(_) => HomePage(),
).initialRoute();
// Add a single guard
router.route(
ProfilePage.path,
(_) => ProfilePage(),
routeGuards: [AuthRouteGuard()],
);
// Add multiple guards (executed in order)
router.route(
AdminPage.path,
(_) => AdminPage(),
routeGuards: [AuthRouteGuard(), AdminRoleGuard()],
);
});
Guards de groupe
Appliquez des guards a plusieurs routes simultanement en utilisant les groupes de routes :
appRouter() => nyRoutes((router) {
router.route(HomePage.path, (_) => HomePage()).initialRoute();
// All routes in this group require authentication
router.group(() {
return {
'prefix': '/dashboard',
'route_guards': [AuthRouteGuard()],
};
}, (router) {
router.route(DashboardPage.path, (_) => DashboardPage());
router.route(SettingsPage.path, (_) => SettingsPage());
router.route(ProfilePage.path, (_) => ProfilePage());
});
});
Composition de Guards
Nylo Website fournit des outils pour composer des guards ensemble afin de creer des patrons reutilisables.
GuardStack
Combinez plusieurs guards en un seul guard reutilisable :
final protectedRoute = GuardStack([
AuthRouteGuard(),
VerifyEmailGuard(),
TwoFactorGuard(),
]);
// Use the stack on a route
router.route(
SecurePage.path,
(_) => SecurePage(),
routeGuards: [protectedRoute],
);
GuardStack execute les guards dans l'ordre. Si un guard retourne handled, les guards restants sont ignores.
ConditionalGuard
Appliquer un guard uniquement lorsqu'une condition est vraie :
router.route(
BetaPage.path,
(_) => BetaPage(),
routeGuards: [
ConditionalGuard(
condition: (context) => context.queryParameters.containsKey("beta"),
guard: BetaAccessGuard(),
),
],
);
Si la condition retourne false, le guard est ignore et la navigation continue.
ParameterizedGuard
Creez des guards qui acceptent des parametres de configuration :
class RoleGuard extends ParameterizedGuard<List<String>> {
RoleGuard(super.params); // params = allowed roles
@override
Future<GuardResult> onBefore(RouteContext context) async {
User? user = await Auth.user<User>();
if (user == null || !params.contains(user.role)) {
return redirect(UnauthorizedPage.path);
}
return next();
}
}
// Usage
router.route(
AdminPage.path,
(_) => AdminPage(),
routeGuards: [RoleGuard(["admin", "super_admin"])],
);
Exemples
Guard d'authentification
class AuthRouteGuard extends NyRouteGuard {
AuthRouteGuard();
@override
Future<GuardResult> onBefore(RouteContext context) async {
bool isAuthenticated = await Auth.isAuthenticated();
if (!isAuthenticated) {
return redirect(HomePage.path);
}
return next();
}
}
Guard d'abonnement avec parametres
class SubscriptionGuard extends ParameterizedGuard<List<String>> {
SubscriptionGuard(super.params);
@override
Future<GuardResult> onBefore(RouteContext context) async {
User? user = await Auth.user<User>();
bool hasAccess = params.any((plan) => user?.subscription == plan);
if (!hasAccess) {
return redirect(UpgradePage.path, data: {"plans": params});
}
setData({"user": user});
return next();
}
}
// Require premium or pro subscription
router.route(
PremiumPage.path,
(_) => PremiumPage(),
routeGuards: [
AuthRouteGuard(),
SubscriptionGuard(["premium", "pro"]),
],
);
Guard de journalisation
class LoggingGuard extends NyRouteGuard {
LoggingGuard();
@override
Future<GuardResult> onBefore(RouteContext context) async {
print("Navigating to: ${context.routeName}");
return next();
}
@override
Future<void> onAfter(RouteContext context) async {
print("Arrived at: ${context.routeName}");
}
}