onRequest method

  1. @override
void onRequest(
  1. RequestOptions options,
  2. RequestInterceptorHandler handler
)

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);
}