Routering

Routes in Finch worden gedefinieerd met FinchRoute-objecten die zijn verzameld in een routeringsfunctie. De routeringsfunctie wordt geregistreerd met app.addRouting() en wordt aangeroepen voor elk binnenkomend verzoek.

Routeringsfunctie

import 'package:finch/finch_route.dart';
import '../controllers/home_controller.dart';

final homeController = HomeController();

Future<List<FinchRoute>> getWebRoute() async {
  return [
    FinchRoute(
      key: 'root',
      path: '/',
      methods: Methods.ONLY_GET,
      index: homeController.index,
    ),
  ];
}

Registreer het in app.dart:

app.addRouting(getWebRoute);

FinchRoute-parameters

Parameter Type Beschrijving
key String Unieke route-identificator. Wordt gebruikt om URL's te genereren ($e.routeUrl('key'))
path String URL-padsegment. Ondersteunt {param} of :param en wildcard *
methods List<String> Toegestane HTTP-methoden. Gebruik Methods-constanten
index Future<String> Function()? Handlerfunctie (geen rq-parameter; gebruik Context.rq of een controllermethode)
controller Controller? Controller-instantie. index() wordt aangeroepen wanneer er geen index is ingesteld
children List<FinchRoute> Geneste kindroutes. Pad is relatief ten opzichte van de ouder
extraPath List<String> Extra paden die naar dezelfde route verwijzen
auth AuthController? Authenticatiebeveiliging voor deze route
permissions List<String> Permissiestrings die na auth worden gecontroleerd
middlewares List<Middleware> Middleware-keten die vóór de handler wordt uitgevoerd
hosts List<String> Beperk tot specifieke hostnamen (['*'] = alle)
ports List<int> Beperk tot specifieke poorten ([] = alle)
params Map<String, dynamic> Standaard sjabloonparameters
excludePaths List<String> Subpaden die zijn uitgesloten van deze route
apiDoc Future<ApiDoc>? Function()? API-documentatie voor Swagger
widget String Pad naar een sjabloonbestand dat direct wordt gerenderd, zonder dat er een controller of index nodig is (zie Widget-routes hieronder)
title String Paginatitel, beschikbaar in sjablonen als {{ $e.pageTitle }}

Gebruik per route slechts één van widget, index of controller. Als er meer dan één is ingesteld, wordt eerst widget gecontroleerd, dan index, en ten slotte controller.index() als terugvaloptie.

HTTP-methoden

// Handige constanten van de Methods-klasse:
Methods.ONLY_GET    // ['GET']
Methods.ONLY_POST   // ['POST']
Methods.ONLY_PUT    // ['PUT']
Methods.ONLY_DELETE // ['DELETE']
Methods.GET_POST    // ['POST', 'GET']
Methods.ALL         // Alle standaard HTTP-methoden

// Of een aangepaste lijst:
methods: [Methods.GET, Methods.POST, Methods.DELETE]

Padparameters

Gebruik de {name}-syntaxis om URL-segmenten vast te leggen. Lees ze in de controller met rq.getParam('name'):

FinchRoute(
  key: 'users.show',
  path: 'users/{id}', // of 'users/:id'
  methods: Methods.ONLY_GET,
  index: userController.show,
),

Opmerking: Dynamische padsegmenten kunnen worden gedefinieerd met de syntax {param} of :param. Beide worden ondersteund en functioneren identiek.

// In UserController:
Future<String> show() async {
  var id = rq.getParam('id');
  return rq.renderData(data: {'id': id});
}

Widget-routes

Wanneer een route alleen een sjabloon moet renderen zonder aangepaste logica, stel dan widget in op een sjabloonpad in plaats van een controller te schrijven:

FinchRoute(
  key: 'about',
  path: 'about',
  title: 'About Us',
  widget: 'pages/about',
),

widget wordt gecontroleerd vóór index en controller, en — in tegenstelling tot die twee — wacht het niet totdat de eigen auth van deze route eerst wordt uitgevoerd; alleen permissies die zijn geërfd van een reeds geauthenticeerde bovenliggende route worden gecontroleerd. Als een widget-route zijn eigen authenticatie nodig heeft, koppel dan auth aan een bovenliggende route in plaats van te vertrouwen op het auth-veld van de widget-route zelf, of gebruik index/controller zodat de auth-controle normaal wordt uitgevoerd.

Wildcardpaden en uitsluitingen

Een pad dat eindigt op * komt overeen met de route zelf en elk subpad daaronder — handig voor catch-all handlers zoals een fallback voor een single-page app of een generieke bestandsproxy:

FinchRoute(
  key: 'spa.fallback',
  path: 'app/*',
  index: homeController.spaIndex,
  excludePaths: ['app/api'], // valt nog steeds door naar andere routes voor dit subpad
),

excludePaths bevat subpaden die niet door de wildcard mogen worden opgeslokt, zodat specifiekere routes (of statische bestanden) onder de wildcard nog steeds bereikbaar blijven.

Geneste routes (children)

Kindpaden zijn relatief ten opzichte van het bovenliggende pad:

FinchRoute(
  key: 'example',
  path: 'example',
  index: homeController.redirectToRoot,
  children: [
    FinchRoute(
      key: 'example.form.get',
      path: 'form',
      methods: Methods.ONLY_GET,
      index: homeController.exampleForm,
    ),
    FinchRoute(
      key: 'example.form.post',
      path: 'form',
      methods: Methods.ONLY_POST,
      index: authController.loginPost,
    ),
    FinchRoute(
      key: 'example.panel',
      path: 'panel',
      methods: Methods.ALL,
      auth: authController,
      permissions: ['admin'],
      index: homeController.exampleAuth,
    ),
  ],
),

Extra paden

extraPath koppelt extra URL-prefixen aan dezelfde route en de bijbehorende kindroutes:

FinchRoute(
  key: 'root.mysql',
  path: 'example/mysql',
  extraPath: ['api/example/mysql'],
  methods: Methods.GET_POST,
  index: homeController.exampleMysql,
),

Meerdere vergelijkbare routes maken

FinchRoute.makeList maakt meerdere routes aan die dezelfde configuratie delen maar alleen verschillen in pad — handig voor aliassen of bijna identieke endpoints:

var routes = FinchRoute.makeList(
  paths: ['users', 'members', 'people'],
  methods: Methods.ONLY_GET,
  controller: UserController(),
  auth: AppAuthController(),
  permissions: ['user.read'],
  key: 'user.alias', // wordt 'user.alias.1', 'user.alias.2', 'user.alias.3'
);

Middleware

Koppel een of meer Middleware-instanties aan een route (of een vloeiende .middleware()-keten) om verzoekfilterlogica uit te voeren voordat de handler wordt uitgevoerd — logging, headercontroles, rate limiting, CORS en vergelijkbare cross-cutting concerns. Zie de Middleware Guide voor de volledige API en de uitvoeringsvolgorde ten opzichte van auth en permissions.

Host- en poortfiltering

Beperk een route tot specifieke hosts of poorten:

FinchRoute(
  key: 'root.localhost',
  path: 'example/host',
  hosts: ['localhost'],
  ports: [80, 8085],
  index: homeController.renderLocalhost,
  methods: Methods.ALL,
),
FinchRoute(
  key: 'root.host',
  path: 'example/host',
  ports: [80, 8085],
  hosts: ['127.0.0.1'],
  index: homeController.render127001,
  methods: Methods.ALL,
),

Authenticatiebeveiliging

Koppel een AuthController om een route te beveiligen. Als auth() false retourneert, wordt het verzoek afgewezen voordat het de handler bereikt:

FinchRoute(
  key: 'admin.panel',
  path: 'admin/panel',
  auth: AppAuthController(),
  permissions: ['admin'],
  index: adminController.panel,
),

Zie Auth Controller voor implementatiedetails.

Route-caching

Gebruik de .cache()-extensie om antwoorden te cachen:

FinchRoute(
  key: 'root.route',
  path: 'example/route',
  methods: Methods.ONLY_GET,
  index: homeController.exampleRoute,
).cache(
  cacheDuration: Duration(minutes: 10),
  cacheType: [CacheParam.path, CacheParam.method, CacheParam.language],
  cacheSource: CacheSource.file,
),

Zie Route Cache voor volledige details.

URL's genereren vanuit routesleutels

Gebruik in sjablonen $e.routeUrl('key') om URL's te genereren:

<a href="{{ $e.routeUrl('example.panel') }}">Paneel</a>
<a href="{{ $e.routeUrl('users.show', {'id': user.id}) }}">Gebruiker tonen</a>

Alle routes ophalen

Haal de volledige lijst met routes op tijdens runtime:

var routes = await app.getAllRoutes();

Hoe matching werkt

Voor elk binnenkomend verzoek doorloopt Finch de routeringslijst die je hebt geregistreerd met app.addRouting() in volgorde, van boven naar beneden:

  1. Host/poortfilter — een route wordt direct overgeslagen als hosts of ports niet overeenkomen met het verzoek.
  2. Padovereenkomst — het path van de route (plus elke extraPath) wordt vergeleken met het aangevraagde pad, waarbij {param}/:param-placeholders en wildcard *-achtervoegsels onderweg worden opgelost.
  3. Als het pad overeenkomt maar methods de HTTP-methode van het verzoek niet bevat, wordt de route behandeld als niet gevonden en gaat de matching verder met de volgende route.
  4. Als de route children heeft, gaat Finch recursief verder zodra het padprefix van de ouder overeenkomt — auth en middlewares van de ouder worden nog steeds eerst uitgevoerd en zijn van toepassing op elk kind.
  5. De eerste route die volledig overeenkomt (pad, methode, host, poort, auth, permissions, middleware) verwerkt het verzoek; er worden geen verdere routes geprobeerd.
  6. Als niets overeenkomt, valt Finch terug op het serveren van een statisch bestand vanuit publicDir, en retourneert een 404 als dat bestand ook niet bestaat.

Omdat de eerste match wint, is de volgorde belangrijk: plaats specifiekere routes vóór bredere wildcardroutes die ze anders zouden kunnen opslokken.