Middleware Gids

Deze gids legt uit hoe je middleware maakt en gebruikt in je Finch-applicatie. Middleware biedt een handig mechanisme om binnenkomende HTTP-verzoeken te inspecteren en te filteren.

Wat is Middleware?

Middleware in Finch is een klasse die zich bevindt tussen het binnenkomende HTTP-verzoek en je route-handler (controller of index-functie). Het stelt je in staat om logica uit te voeren voordat een verzoek je controller bereikt — bijvoorbeeld het controleren van headers, het loggen van verzoeken, het valideren van tokens, het aanpassen van verzoekparameters of het blokkeren van ongeautoriseerde toegang.

Elke middleware-klasse moet de abstracte Middleware klasse uitbreiden en de handle() methode implementeren.

Hoe Middleware Werkt

Wanneer een verzoek overeenkomt met een route waaraan middleware is gekoppeld, voert Finch de middleware-pipeline op volgorde uit voordat het verzoek wordt doorgegeven aan de controller of index-functie. De stroom is als volgt:

  1. Route-patroon matching
  2. HTTP-methode, host en poort validatie
  3. Authenticatiecontrole (auth)
  4. Middleware-uitvoering (de handle() methode van elke middleware wordt opeenvolgend uitgevoerd)
  5. Machtigingscontrole
  6. Controller/index uitvoering

Retourwaarden

De handle() methode retourneert een Future<String?>:

  • Retourneer null — het verzoek gaat door en gaat verder naar de volgende middleware of naar de route-handler.
  • Retourneer een niet-lege String — het verzoek wordt geblokkeerd. De middleware-pipeline stopt en de route wordt niet gematcht.

Als er meerdere middlewares aan een route zijn gekoppeld, worden ze opeenvolgend uitgevoerd. Als een middleware een niet-lege string retourneert, worden de resterende middlewares overgeslagen en wordt het verzoek afgewezen.

Een Middleware Maken

Om een middleware te maken, breid je de Middleware klasse uit en overschrijf je de handle() methode:

import 'package:finch/finch_route.dart';

class MyMiddleware extends Middleware {
  @override
  Future<String?> handle() async {
    // Je logica hier
    return null; // Laat het verzoek doorgaan
  }
}

Toegang tot het Verzoek

Binnen een middleware kun je het huidige verzoek benaderen via rq (beschikbaar gesteld door de Middleware basisklasse):

class LogMiddleware extends Middleware {
  @override
  Future<String?> handle() async {
    print('Verzoek: ${rq.method} ${rq.uri.path}');
    return null;
  }
}

Parameters Toevoegen

Je kunt vanuit een middleware data aan het verzoek toevoegen met rq.addParam(). Deze data is beschikbaar in controllers en templates verderop in de keten:

class TestMiddleware extends Middleware {
  @override
  Future<String?> handle() async {
    rq.addParam('middleware', 'Test Middleware Active');
    return null;
  }
}

Een Verzoek Blokkeren

Retourneer een niet-lege string om te voorkomen dat het verzoek de route-handler bereikt:

class ApiKeyMiddleware extends Middleware {
  @override
  Future<String?> handle() async {
    final apiKey = rq.header('X-API-Key');
    if (apiKey != 'my-secret-key') {
      rq.renderError(401, message: 'Ongeldige API-sleutel');
      return 'Unauthorized'; // Blokkeert het verzoek
    }
    return null; // Laat het verzoek doorgaan
  }
}

Middleware aan Routes Koppelen

Er zijn twee manieren om middleware aan een route te koppelen.

1. Via de Constructor

Geef een lijst van middleware-instanties door aan de middlewares parameter van FinchRoute:

final testMiddleware = TestMiddleware();
final logMiddleware = LogMiddleware();

FinchRoute(
  path: '/info',
  index: homeController.info,
  middlewares: [testMiddleware, logMiddleware],
);

2. Via de Fluent API

Gebruik de .middleware() methode om middleware aan een route te koppelen:

FinchRoute(
  path: '/info',
  index: homeController.info,
).middleware(TestMiddleware()).middleware(LogMiddleware());

Middleware op Bovenliggende Routes

Wanneer middleware is gekoppeld aan een bovenliggende route met children, wordt de middleware uitgevoerd voordat een onderliggende route wordt verwerkt. Hiermee kun je gedeelde logica (zoals authenticatie of logging) toepassen op een hele groep routes:

FinchRoute(
  path: '/admin',
  middlewares: [AdminMiddleware()],
  children: [
    FinchRoute(
      path: '/dashboard',
      index: adminController.dashboard,
    ),
    FinchRoute(
      path: '/users',
      index: adminController.users,
    ),
  ],
);

In dit voorbeeld wordt AdminMiddleware uitgevoerd vóór elk verzoek naar /admin/dashboard of /admin/users.

Praktische Voorbeelden

Rate Limiting Middleware

class RateLimitMiddleware extends Middleware {
  static final Map<String, List<DateTime>> _requests = {};
  final int maxRequests;
  final Duration window;

  RateLimitMiddleware({
    this.maxRequests = 100,
    this.window = const Duration(minutes: 1),
  });

  @override
  Future<String?> handle() async {
    final ip = rq.clientIP;
    final now = DateTime.now();
    _requests[ip] = (_requests[ip] ?? [])
      ..removeWhere((t) => now.difference(t) > window)
      ..add(now);

    if (_requests[ip]!.length > maxRequests) {
      rq.renderError(429, message: 'Te veel verzoeken');
      return 'Rate limit exceeded';
    }
    return null;
  }
}

CORS Middleware

class CorsMiddleware extends Middleware {
  final String allowedOrigin;

  CorsMiddleware({this.allowedOrigin = '*'});

  @override
  Future<String?> handle() async {
    rq.response.headers.add('Access-Control-Allow-Origin', allowedOrigin);
    rq.response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    rq.response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    return null;
  }
}

Onderhoudsmodus Middleware

class MaintenanceMiddleware extends Middleware {
  final bool isEnabled;

  MaintenanceMiddleware({this.isEnabled = false});

  @override
  Future<String?> handle() async {
    if (isEnabled) {
      rq.renderError(503, message: 'Service is in onderhoud');
      return 'Maintenance mode';
    }
    return null;
  }
}

Middleware vs Auth Controller

Eigenschap Middleware Auth Controller
Doel Algemene verzoekfiltering Authenticatie & autorisatie
Uitvoeringsvolgorde Na auth, vóór machtigingen Vóór middleware
Retourtype Future<String?> Future<bool>
Kan verzoek wijzigen Ja (rq.addParam) Ja
Kan fouten weergeven Ja Ja
Meerdere per route Ja (lijst) Eén per route
Overgeërfd door kinderen Ja Ja

Gebruik Auth Controller wanneer je volledige authenticatie- en sessiebeheer nodig hebt. Gebruik Middleware voor al het andere — logging, headervalidatie, rate limiting, CORS, feature flags, enzovoort.

Volledig Voorbeeld

Hier is een compleet voorbeeld van het gebruik van middleware in een Finch-applicatie:

1. Maak middleware-klassen:

// lib/middleware/test_middleware.dart
import 'package:finch/finch_route.dart';

class TestMiddleware extends Middleware {
  @override
  Future<String?> handle() async {
    rq.addParam('middleware', 'Test Middleware Active');
    return null;
  }
}

2. Registreer middleware in je routes:

// lib/route/web_route.dart
import '../middleware/test_middleware.dart';

final testMiddleware = TestMiddleware();

List<FinchRoute> routes = [
  FinchRoute(
    key: 'root.info',
    path: 'info',
    extraPath: ['api/info'],
    index: homeController.info,
    middlewares: [testMiddleware],
  ),
];

3. Gebruik middleware-data in je controller:

class HomeController extends Controller {
  Future<String> info() async {
    final middlewareParam = rq.get('middleware');
    return rq.renderString(text: 'Middleware zegt: $middlewareParam');
  }
}

API Referentie

Middleware (abstracte klasse)

Lid Type Beschrijving
rq Request (getter) Toegang tot het huidige HTTP-verzoekobject
handle() Future<String?> Overschrijf om middleware-logica te implementeren. Retourneer null om door te gaan, niet-lege string om te blokkeren.

FinchRoute Middleware Leden

Lid Type Beschrijving
middlewares List<Middleware> Lijst van middleware-instanties gekoppeld aan de route
middleware(m) FinchRoute Fluent-methode om een middleware toe te voegen; retourneert de route voor chaining
handleMiddlewares() Future<bool> Voert alle middleware op volgorde uit; retourneert true als alle zijn geslaagd