dvRunMiddlewares function
Runs a route's declared middleware in the order it declared them.
Throws when a key nothing implements reaches this: the generator refuses such a build, and a request arriving with one anyway means a generated file is older than the framework running it. Serving it as though the middleware applied is the failure this whole file exists to end.
Implementation
Future<DVMiddlewareResult> dvRunMiddlewares(
List<String> keys,
Object? request,
) async {
final MiddlewareChain chain = MiddlewareChain();
for (final String key in keys) {
if (dvMiddlewareKeysAlwaysOn.contains(key)) continue;
// Enforced by the generated prelude, before this chain is reached.
if (dvMiddlewareKeysAtRequest.contains(key)) continue;
// Wrapped around the handler and this chain both.
if (dvMiddlewareKeysWrapping.contains(key)) continue;
final Middleware? middleware = dvMiddlewareFor(key);
if (middleware == null) {
throw ArgumentError.value(
key,
'middleware',
dvMiddlewareKeysUnbuiltReason[key] ??
'is not a middleware this version of Dartvel implements.',
);
}
chain.use(middleware);
}
final MiddlewareContext context = await chain.execute(request);
for (final MapEntry<String, int> refusal in _refusalStatus.entries) {
if (!context.data.containsKey(refusal.key)) continue;
return DVMiddlewareResult(
allowed: false,
status: refusal.value,
message: _refusalMessage[refusal.key]!,
// Nothing from a refused chain decorates a response that is not being
// sent, except when to come back after a rate limit (RFC 9110 10.2.3).
headers: <String, String>{
if (refusal.key == 'rateLimitError' && context.data['retryAfter'] is int)
'Retry-After': '${context.data['retryAfter']}',
},
data: const <String, Object?>{},
);
}
final Map<String, String> headers = <String, String>{};
final Object? csp = context.data['cspHeader'];
if (csp is String) headers['Content-Security-Policy'] = csp;
final Object? security = context.data['securityHeaders'];
if (security is Map) {
for (final MapEntry<Object?, Object?> entry in security.entries) {
final Object? name = entry.key;
final Object? value = entry.value;
if (name is String && value is String) headers[name] = value;
}
}
return DVMiddlewareResult(
allowed: context.shouldContinue,
status: 200,
message: '',
headers: headers,
data: Map<String, Object?>.unmodifiable(context.data),
);
}