Route Guards
Einleitung
Route Guards bieten Middleware für die Navigation in Nylo Website. Sie fangen Routenübergänge ab und ermöglichen es Ihnen zu steuern, ob ein Benutzer auf eine Seite zugreifen darf, ihn auf eine andere Seite umzuleiten oder die an eine Route übergebenen Daten zu ändern.
Häufige Anwendungsfälle sind:
- Authentifizierungsprüfungen -- nicht authentifizierte Benutzer auf eine Anmeldeseite umleiten
- Rollenbasierter Zugriff -- Seiten auf Admin-Benutzer beschränken
- Datenvalidierung -- sicherstellen, dass erforderliche Daten vor der Navigation vorhanden sind
- Datenanreicherung -- zusätzliche Daten an eine Route anfügen
Guards werden der Reihe nach ausgeführt, bevor die Navigation stattfindet. Wenn ein Guard handled zurückgibt, wird die Navigation gestoppt (entweder durch Umleitung oder Abbruch).
Einen Route Guard erstellen
Erstellen Sie einen Route Guard mit der Metro-CLI:
metro make:route_guard auth
Dies erzeugt eine Guard-Datei:
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();
}
}
Guard-Lebenszyklus
Jeder Route Guard hat drei Lebenszyklusmethoden:
onBefore
Wird vor der Navigation aufgerufen. Hier prüfen Sie Bedingungen und entscheiden, ob die Navigation erlaubt, umgeleitet oder abgebrochen werden soll.
@override
Future<GuardResult> onBefore(RouteContext context) async {
bool isLoggedIn = await Auth.isAuthenticated();
if (!isLoggedIn) {
return redirect(HomePage.path);
}
return next();
}
Rückgabewerte:
next()-- zum nächsten Guard fortfahren oder zur Route navigierenredirect(path)-- zu einer anderen Route umleitenabort()-- Navigation vollständig abbrechen
onAfter
Wird nach erfolgreicher Navigation aufgerufen. Verwenden Sie dies für Analysen, Protokollierung oder Nebeneffekte nach der Navigation.
@override
Future<void> onAfter(RouteContext context) async {
// Log page view
Analytics.trackPageView(context.routeName);
}
onLeave
Wird aufgerufen, wenn der Benutzer eine Route verlässt. Geben Sie false zurück, um das Verlassen zu verhindern.
@override
Future<bool> onLeave(RouteContext context) async {
if (hasUnsavedChanges) {
// Show confirmation dialog
return await showConfirmDialog();
}
return true; // Allow leaving
}
RouteContext
Das RouteContext-Objekt wird an alle Guard-Lebenszyklusmethoden übergeben und enthält Informationen über die Navigation:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
context |
BuildContext? |
Aktueller Build-Kontext |
data |
dynamic |
An die Route übergebene Daten |
queryParameters |
Map<String, String> |
URL-Abfrageparameter |
routeName |
String |
Name/Pfad der Zielroute |
originalRouteName |
String? |
Ursprünglicher Routenname vor Transformationen |
@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();
}
RouteContext transformieren
Erstellen Sie eine Kopie mit anderen Daten:
// Change the data type
RouteContext<User> userContext = context.withData<User>(currentUser);
// Copy with modified fields
RouteContext updated = context.copyWith(
data: enrichedData,
queryParameters: {"tab": "settings"},
);
Guard-Aktionen
next
Zum nächsten Guard in der Kette fortfahren oder zur Route navigieren, wenn dies der letzte Guard ist:
return next();
redirect
Den Benutzer zu einer anderen Route umleiten:
return redirect(LoginPage.path);
Mit zusätzlichen Optionen:
return redirect(
LoginPage.path,
data: {"returnTo": context.routeName},
navigationType: NavigationType.pushReplace,
queryParameters: {"source": "guard"},
);
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
path |
Object |
erforderlich | Routenpfad-String oder RouteView |
data |
dynamic |
null | Daten, die an die Umleitungsroute übergeben werden |
queryParameters |
Map<String, dynamic>? |
null | Abfrageparameter |
navigationType |
NavigationType |
pushReplace |
Navigationsmethode |
result |
dynamic |
null | Zurückzugebendes Ergebnis |
removeUntilPredicate |
Function? |
null | Prädikat zur Routenentfernung |
transitionType |
TransitionType? |
null | Seitenübergangstyp |
onPop |
Function(dynamic)? |
null | Callback beim Zurücknavigieren |
abort
Navigation abbrechen, ohne umzuleiten. Der Benutzer bleibt auf seiner aktuellen Seite:
return abort();
setData
Die Daten ändern, die an nachfolgende Guards und die Zielroute übergeben werden:
@override
Future<GuardResult> onBefore(RouteContext context) async {
User user = await fetchUser();
// Enrich the route data
setData({"user": user, "originalData": context.data});
return next();
}
Guards auf Routen anwenden
Fügen Sie Guards zu einzelnen Routen in Ihrer Router-Datei hinzu:
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()],
);
});
Gruppen-Guards
Wenden Sie Guards auf mehrere Routen gleichzeitig an, indem Sie Routengruppen verwenden:
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());
});
});
Guard-Komposition
Nylo Website bietet Werkzeuge, um Guards für wiederverwendbare Muster zusammenzusetzen.
GuardStack
Kombinieren Sie mehrere Guards zu einem einzigen wiederverwendbaren Guard:
final protectedRoute = GuardStack([
AuthRouteGuard(),
VerifyEmailGuard(),
TwoFactorGuard(),
]);
// Use the stack on a route
router.route(
SecurePage.path,
(_) => SecurePage(),
routeGuards: [protectedRoute],
);
GuardStack führt Guards der Reihe nach aus. Wenn ein Guard handled zurückgibt, werden die verbleibenden Guards übersprungen.
ConditionalGuard
Wenden Sie einen Guard nur an, wenn eine Bedingung erfüllt ist:
router.route(
BetaPage.path,
(_) => BetaPage(),
routeGuards: [
ConditionalGuard(
condition: (context) => context.queryParameters.containsKey("beta"),
guard: BetaAccessGuard(),
),
],
);
Wenn die Bedingung false zurückgibt, wird der Guard übersprungen und die Navigation fortgesetzt.
ParameterizedGuard
Erstellen Sie Guards, die Konfigurationsparameter akzeptieren:
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"])],
);
Beispiele
Authentifizierungs-Guard
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();
}
}
Abonnement-Guard mit Parametern
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"]),
],
);
Protokollierungs-Guard
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}");
}
}