onRequest method
Called when the request is about to be sent.
Implementation
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) {
if (!ApiTrace.enabled ||
(deferToHttpOverrides && InterceptorCoordination.httpOverridesActive)) {
// Either tracing is off, or TracedHttpOverrides is installed
// and will record this request at the dart:io layer —
// recording here too would duplicate the timeline entry.
handler.next(options);
return;
}
// Tag with the current route scope (REQ-RS-002).
final scope = RouteScope.current;
if (scope != null) {
options.extra['routeScopeId'] = scope.scopeId;
options.extra['routeName'] = scope.routeName;
}
final completer = Completer<Response<dynamic>>();
options.extra['_apiTraceCompleter'] = completer;
// Snapshot request data at request time: `options.data` and
// `options.headers` may be mutated by later interceptors, and
// the record must reflect what this request carried.
final requestBody = _encodeBody(options.data);
final requestHeaders = <String, String>{
for (final MapEntry<String, dynamic> e in options.headers.entries)
e.key: '${e.value}',
};
// Fire-and-forget: ApiTrace.call resolves when the completer
// completes (i.e. when onResponse or onError fires). We do
// not await it because the interceptor must return
// synchronously to `handler.next(options)`.
unawaited(
ApiTrace.call(
_label(options),
method: options.method,
url: options.uri,
execute: () async {
final response = await completer.future;
return ApiTraceResponse(
statusCode: response.statusCode ?? 0,
requestHeaders: requestHeaders,
responseHeaders: _collapseHeaders(response.headers.map),
requestBody: requestBody,
responseBody: _encodeBody(response.data),
);
},
// Forward options.extra (which now carries routeScopeId /
// routeName when a RouteScope is active) onto the record's
// extra map (REQ-RS-002 / TASK-RS-003). By the time this
// executes (after `await completer.future` inside
// `execute`), `_apiTraceCompleter` has already been
// removed from this same map by onResponse/onError, so no
// internal bookkeeping key leaks onto the record.
extra: options.extra,
),
);
handler.next(options);
}