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:
- Route-patroon matching
- HTTP-methode, host en poort validatie
- Authenticatiecontrole (
auth) - Middleware-uitvoering (de
handle()methode van elke middleware wordt opeenvolgend uitgevoerd) - Machtigingscontrole
- 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 |