Route Guards
Pengantar
Route guard menyediakan middleware untuk navigasi di Nylo Website. Route guard mengintersepsi transisi rute dan memungkinkan Anda mengontrol apakah pengguna dapat mengakses halaman, mengarahkan mereka ke tempat lain, atau memodifikasi data yang diteruskan ke rute.
Kasus penggunaan umum meliputi:
- Pemeriksaan autentikasi -- mengarahkan pengguna yang belum terautentikasi ke halaman login
- Akses berbasis peran -- membatasi halaman hanya untuk pengguna admin
- Validasi data -- memastikan data yang diperlukan ada sebelum navigasi
- Pengayaan data -- melampirkan data tambahan ke rute
Guard dieksekusi secara berurutan sebelum navigasi terjadi. Jika guard mana pun mengembalikan handled, navigasi berhenti (baik dengan mengarahkan ulang atau membatalkan).
Membuat Route Guard
Buat route guard menggunakan Metro CLI:
metro make:route_guard auth
Ini menghasilkan file 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();
}
}
Siklus Hidup Guard
Setiap route guard memiliki tiga metode siklus hidup:
onBefore
Dipanggil sebelum navigasi terjadi. Di sinilah Anda memeriksa kondisi dan memutuskan apakah akan mengizinkan, mengarahkan ulang, atau membatalkan navigasi.
@override
Future<GuardResult> onBefore(RouteContext context) async {
bool isLoggedIn = await Auth.isAuthenticated();
if (!isLoggedIn) {
return redirect(HomePage.path);
}
return next();
}
Nilai kembalian:
next()-- lanjutkan ke guard berikutnya atau navigasi ke ruteredirect(path)-- arahkan ulang ke rute yang berbedaabort()-- batalkan navigasi sepenuhnya
onAfter
Dipanggil setelah navigasi berhasil. Gunakan ini untuk analitik, pencatatan, atau efek samping pasca-navigasi.
@override
Future<void> onAfter(RouteContext context) async {
// Log page view
Analytics.trackPageView(context.routeName);
}
onLeave
Dipanggil ketika pengguna meninggalkan rute. Kembalikan false untuk mencegah pengguna meninggalkan halaman.
@override
Future<bool> onLeave(RouteContext context) async {
if (hasUnsavedChanges) {
// Show confirmation dialog
return await showConfirmDialog();
}
return true; // Allow leaving
}
RouteContext
Objek RouteContext diteruskan ke semua metode siklus hidup guard dan berisi informasi tentang navigasi:
| Properti | Tipe | Deskripsi |
|---|---|---|
context |
BuildContext? |
Build context saat ini |
data |
dynamic |
Data yang diteruskan ke rute |
queryParameters |
Map<String, String> |
Parameter query URL |
routeName |
String |
Nama/path rute tujuan |
originalRouteName |
String? |
Nama rute asli sebelum transformasi |
@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();
}
Mentransformasi Route Context
Membuat salinan dengan data yang berbeda:
// Change the data type
RouteContext<User> userContext = context.withData<User>(currentUser);
// Copy with modified fields
RouteContext updated = context.copyWith(
data: enrichedData,
queryParameters: {"tab": "settings"},
);
Aksi Guard
next
Lanjutkan ke guard berikutnya dalam rantai, atau navigasi ke rute jika ini adalah guard terakhir:
return next();
redirect
Arahkan ulang pengguna ke rute yang berbeda:
return redirect(LoginPage.path);
Dengan opsi tambahan:
return redirect(
LoginPage.path,
data: {"returnTo": context.routeName},
navigationType: NavigationType.pushReplace,
queryParameters: {"source": "guard"},
);
| Parameter | Tipe | Default | Deskripsi |
|---|---|---|---|
path |
Object |
required | String path rute atau RouteView |
data |
dynamic |
null | Data yang diteruskan ke rute redirect |
queryParameters |
Map<String, dynamic>? |
null | Parameter query |
navigationType |
NavigationType |
pushReplace |
Metode navigasi |
result |
dynamic |
null | Hasil yang dikembalikan |
removeUntilPredicate |
Function? |
null | Predikat penghapusan rute |
transitionType |
TransitionType? |
null | Tipe transisi halaman |
onPop |
Function(dynamic)? |
null | Callback saat pop |
abort
Batalkan navigasi tanpa mengarahkan ulang. Pengguna tetap di halaman saat ini:
return abort();
setData
Memodifikasi data yang akan diteruskan ke guard berikutnya dan rute tujuan:
@override
Future<GuardResult> onBefore(RouteContext context) async {
User user = await fetchUser();
// Enrich the route data
setData({"user": user, "originalData": context.data});
return next();
}
Menerapkan Guard ke Route
Tambahkan guard ke rute individual di file router Anda:
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()],
);
});
Guard Grup
Terapkan guard ke beberapa rute sekaligus menggunakan grup rute:
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());
});
});
Komposisi Guard
Nylo Website menyediakan alat untuk menyusun guard bersama-sama menjadi pola yang dapat digunakan kembali.
GuardStack
Gabungkan beberapa guard menjadi satu guard yang dapat digunakan kembali:
final protectedRoute = GuardStack([
AuthRouteGuard(),
VerifyEmailGuard(),
TwoFactorGuard(),
]);
// Use the stack on a route
router.route(
SecurePage.path,
(_) => SecurePage(),
routeGuards: [protectedRoute],
);
GuardStack mengeksekusi guard secara berurutan. Jika ada guard yang mengembalikan handled, guard yang tersisa akan dilewati.
ConditionalGuard
Terapkan guard hanya ketika kondisi bernilai true:
router.route(
BetaPage.path,
(_) => BetaPage(),
routeGuards: [
ConditionalGuard(
condition: (context) => context.queryParameters.containsKey("beta"),
guard: BetaAccessGuard(),
),
],
);
Jika kondisi mengembalikan false, guard akan dilewati dan navigasi berlanjut.
ParameterizedGuard
Buat guard yang menerima parameter konfigurasi:
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"])],
);
Contoh
Guard Autentikasi
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 Langganan dengan Parameter
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 Pencatatan
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}");
}
}