Advanced

Route Guards

Giriş

Route guard'lar, Nylo Website içinde navigasyon için middleware sağlar. Rota geçişlerini yakalar ve bir kullanıcının sayfaya erişip erişemeyeceğini, başka bir yere yönlendirilip yönlendirilmeyeceğini veya rotaya aktarılan verilerin değiştirilip değiştirilmeyeceğini kontrol etmenize olanak tanır.

Yaygın kullanım senaryoları şunlardır:

  • Kimlik doğrulama kontrolleri -- kimliği doğrulanmamış kullanıcıları giriş sayfasına yönlendirme
  • Rol tabanlı erişim -- sayfaları yönetici kullanıcılarla sınırlama
  • Veri doğrulama -- navigasyondan önce gerekli verilerin mevcut olduğundan emin olma
  • Veri zenginleştirme -- rotaya ek veri ekleme

Guard'lar navigasyon gerçekleşmeden önce sırayla yürütülür. Herhangi bir guard handled döndürürse, navigasyon durur (yönlendirme veya iptal ile).

Route Guard Oluşturma

Metro CLI kullanarak bir route guard oluşturun:

metro make:route_guard auth

Bu, bir guard dosyası oluşturur:

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 Yaşam Döngüsü

Her route guard'ın üç yaşam döngüsü metodu vardır:

onBefore

Navigasyon gerçekleşmeden önce çağrılır. Koşulları kontrol ettiğiniz ve navigasyona izin verme, yönlendirme veya iptal etme kararını aldığınız yerdir.

@override
Future<GuardResult> onBefore(RouteContext context) async {
  bool isLoggedIn = await Auth.isAuthenticated();

  if (!isLoggedIn) {
    return redirect(HomePage.path);
  }

  return next();
}

Dönüş değerleri:

  • next() -- zincirdeki bir sonraki guard'a devam et veya rotaya yönel
  • redirect(path) -- farklı bir rotaya yönlendir
  • abort() -- navigasyonu tamamen iptal et

onAfter

Başarılı navigasyondan sonra çağrılır. Analitik, günlükleme veya navigasyon sonrası yan etkiler için kullanın.

@override
Future<void> onAfter(RouteContext context) async {
  // Log page view
  Analytics.trackPageView(context.routeName);
}

onLeave

Kullanıcı bir rotadan ayrılırken çağrılır. Kullanıcının ayrılmasını engellemek için false döndürün.

@override
Future<bool> onLeave(RouteContext context) async {
  if (hasUnsavedChanges) {
    // Show confirmation dialog
    return await showConfirmDialog();
  }
  return true; // Allow leaving
}

RouteContext

RouteContext nesnesi tüm guard yaşam döngüsü metotlarına iletilir ve navigasyon hakkında bilgi içerir:

Özellik Tür Açıklama
context BuildContext? Mevcut build context
data dynamic Rotaya aktarılan veri
queryParameters Map<String, String> URL sorgu parametreleri
routeName String Hedef rotanın adı/yolu
originalRouteName String? Dönüşümlerden önceki orijinal rota adı
@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();
}

Route Context'i Dönüştürme

Farklı veriyle bir kopya oluşturun:

// 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 Eylemleri

next

Zincirdeki bir sonraki guard'a devam et veya bu son guard ise rotaya yönel:

return next();

redirect

Kullanıcıyı farklı bir rotaya yönlendir:

return redirect(LoginPage.path);

Ek seçeneklerle:

return redirect(
  LoginPage.path,
  data: {"returnTo": context.routeName},
  navigationType: NavigationType.pushReplace,
  queryParameters: {"source": "guard"},
);
Parametre Tür Varsayılan Açıklama
path Object zorunlu Rota yolu dizesi veya RouteView
data dynamic null Yönlendirme rotasına aktarılacak veri
queryParameters Map<String, dynamic>? null Sorgu parametreleri
navigationType NavigationType pushReplace Navigasyon yöntemi
result dynamic null Döndürülecek sonuç
removeUntilPredicate Function? null Rota kaldırma koşulu
transitionType TransitionType? null Sayfa geçiş türü
onPop Function(dynamic)? null Pop'ta geri çağırma

abort

Yönlendirme yapmadan navigasyonu iptal et. Kullanıcı mevcut sayfasında kalır:

return abort();

setData

Sonraki guard'lara ve hedef rotaya aktarılacak verileri değiştirin:

@override
Future<GuardResult> onBefore(RouteContext context) async {
  User user = await fetchUser();

  // Enrich the route data
  setData({"user": user, "originalData": context.data});

  return next();
}

Route'lara Guard Uygulama

Router dosyanızda bireysel rotalara guard ekleyin:

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()],
  );
});

Grup Guard'ları

Rota grupları kullanarak birden fazla rotaya aynı anda guard uygulayın:

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 Kompozisyonu

Nylo Website, yeniden kullanılabilir kalıplar için guard'ları birleştirme araçları sağlar.

GuardStack

Birden fazla guard'ı tek bir yeniden kullanılabilir guard olarak birleştirin:

final protectedRoute = GuardStack([
  AuthRouteGuard(),
  VerifyEmailGuard(),
  TwoFactorGuard(),
]);

// Use the stack on a route
router.route(
  SecurePage.path,
  (_) => SecurePage(),
  routeGuards: [protectedRoute],
);

GuardStack guard'ları sırayla yürütür. Herhangi bir guard handled döndürürse, kalan guard'lar atlanır.

ConditionalGuard

Bir guard'ı yalnızca belirli bir koşul doğru olduğunda uygulayın:

router.route(
  BetaPage.path,
  (_) => BetaPage(),
  routeGuards: [
    ConditionalGuard(
      condition: (context) => context.queryParameters.containsKey("beta"),
      guard: BetaAccessGuard(),
    ),
  ],
);

Koşul false döndürürse, guard atlanır ve navigasyon devam eder.

ParameterizedGuard

Yapılandırma parametreleri alan guard'lar oluşturun:

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"])],
);

Örnekler

Kimlik Doğrulama 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();
  }
}

Parametreli Abonelik Guard'ı

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"]),
  ],
);

Günlükleme 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}");
  }
}