Dio分析-Interceptors

官方描述

Dio设计的拦截器[Interceptor]用于协助处理[RequestOptions]http请求发送配置, [response]http响应,[DioException]过程异常。
这些拦截器在每次请求和响应之间仅被调用一次,意味着重定向不会触发他们的行为。
其他设计
[InteceptorsWrapper],拦截器封装,帮助创建拦截器。
[QueuedInterceptor]队列式拦截器,以task形式在队列中实现拦截器行为。
[QueuedInterceptorsWrapper],队列式拦截器封装,帮助创建队列式拦截器。

[Interceptor] helps to deal with [RequestOptions], [Response], and [DioException] during the lifecycle of a request before it reaches users.
Interceptors are called once per request and response, that means redirects aren’t triggering interceptors.
See also:
[InterceptorsWrapper], the helper class to create [Interceptor]s.
[QueuedInterceptor], resolves interceptors as a task in the queue.
[QueuedInterceptorsWrapper], the helper class to create [QueuedInterceptor]s.

调用实例

在以下代码中,充分体现了OnRequest,OnResponse,OnError三种拦截时机下,通过handler.next, handler.resolve,handler.reject对于整个过程的拦截器处理(请求继续),拦截完结(结束请求),拦截抛出异常(结束请求)

    test('interceptor chain', () async {
      final dio = Dio();
      dio.options.baseUrl = EchoAdapter.mockBase;
      dio.httpClientAdapter = EchoAdapter();
      dio.interceptors
        ..add(
          InterceptorsWrapper(
            onRequest: (reqOpt, handler) {
              switch (reqOpt.path) {
                case '/resolve':
                  handler.resolve(Response(requestOptions: reqOpt, data: 1));
                  break;
                case '/resolve-next':
                  handler.resolve(
                    Response(requestOptions: reqOpt, data: 2),
                    true,
                  );
                  break;
                case '/resolve-next/always':
                  handler.resolve(
                    Response(requestOptions: reqOpt, data: 2),
                    true,
                  );
                  break;
                case '/resolve-next/reject':
                  handler.resolve(
                    Response(requestOptions: reqOpt, data: 2),
                    true,
                  );
                  break;
                case '/resolve-next/reject-next':
                  handler.resolve(
                    Response(requestOptions: reqOpt, data: 2),
                    true,
                  );
                  break;
                case '/reject':
                  handler
                      .reject(DioException(requestOptions: reqOpt, error: 3));
                  break;
                case '/reject-next':
                  handler.reject(
                    DioException(requestOptions: reqOpt, error: 4),
                    true,
                  );
                  break;
                case '/reject-next/reject':
                  handler.reject(
                    DioException(requestOptions: reqOpt, error: 5),
                    true,
                  );
                  break;
                case '/reject-next-response':
                  handler.reject(
                    DioException(requestOptions: reqOpt, error: 5),
                    true,
                  );
                  break;
                default:
                  handler.next(reqOpt); //continue
              }
            },
            onResponse: (response, ResponseInterceptorHandler handler) {
              final options = response.requestOptions;
              switch (options.path) {
                case '/resolve':
                  throw 'unexpected1';
                case '/resolve-next':
                  response.data++;
                  handler.resolve(response); //3
                  break;
                case '/resolve-next/always':
                  response.data++;
                  handler.next(response); //3
                  break;
                case '/resolve-next/reject':
                  handler.reject(
                    DioException(
                      requestOptions: options,
                      error: '/resolve-next/reject',
                    ),
                  );
                  break;
                case '/resolve-next/reject-next':
                  handler.reject(
                    DioException(requestOptions: options, error: ''),
                    true,
                  );
                  break;
                default:
                  handler.next(response); //continue
              }
            },
            onError: (error, handler) {
              if (error.requestOptions.path == '/reject-next-response') {
                handler.resolve(
                  Response(
                    requestOptions: error.requestOptions,
                    data: 100,
                  ),
                );
              } else if (error.requestOptions.path ==
                  '/resolve-next/reject-next') {
                handler.next(error.copyWith(error: 1));
              } else {
                if (error.requestOptions.path == '/reject-next/reject') {
                  handler.reject(error);
                } else {
                  int count = error.error as int;
                  count++;
                  handler.next(error.copyWith(error: count));
                }
              }
            },
          ),

设计理念

  • Interceptor是用户编写针对不同时机的一些处理。如上面例子中的InterceptorWrapper()包裹的代码块。
  • DioMixin中的Interceptor是以列表形式存在,通过future.then进行链式调用,于是涉及到了Interceptor的链式调用的结果流转和终止的逻辑。
  final Interceptors _interceptors = Interceptors();
  dio.interceptors..add
  • 程序角度.每个Interceptor可以针对三个时机来处理. [OnRequest] 网络连接和请求过程,[onResponse] 响应结果处理过程, [onError] 异常处理过程。

那好,现在的问题是,如何完成多个Interceptor之间的链式传递,

  • 需要定义链式传递的结果类型InterceptorState.T为结果类型,而InterceptorState.type定义链式传递的行为(next 向下流转, resolve 完结, reject异常终止)。通过completer就可以实现结果的链式传递。
  • 但是这需要在Interceptor方法,构造这样一个结果并进行传递,这会大大增加Interceptor使用的复杂度。
    于是设计handler这种通用的处理器提供给用户Interceptor中调用使用,_BaseHandler设计出来就是为了这个目的,利用completer来串联整个拦截器序列的执行和结果流转。继承_BaseHandler的处理器需要支持跟流传行为相同的方法next, resolve, reject。
  • 由于用户的拦截器还针对不同的时机传递的结果类型也有所不相同,于是设计了三个继承于_BaseHandler的子令牌[RequestInterceptorHandler]针对onRequest时机, [ResponseInterceptorHandler]针对onResponse时机, [ErrorInterceptorHandler]针对onError时机,在不同的时机把对应的handler通过传递给业务的Interceptor类似行为令牌并以参数形式传递给每个Iteration,用于在Iterator执行完毕后调用,用于传递结果和控制拦截器顺序执行的流转。handler要求业务的拦截器执行完毕后调用,并确定流转的结果next,resolve,reject。
  • 而InterceptorWrapper就是额外用于规范业务定义的Interceptor的执行规范,在不同的时机向Interceptor专递行为处理器(handler),并要求Interceptor在执行完成调用该行为令牌。

对于时机流转的控制,可以参见Dio_Mixin的request方法

    FutureOr Function(dynamic) requestInterceptorWrapper(
      InterceptorSendCallback interceptor,
    ) {
      return (dynamic incomingState) async {
        final state = incomingState as InterceptorState;
        if (state.type == InterceptorResultType.next) {
          return listenCancelForAsyncTask(
            requestOptions.cancelToken,
            Future(() {
              final requestHandler = RequestInterceptorHandler();
              interceptor(state.data as RequestOptions, requestHandler);
              return requestHandler.future;
            }),
          );
        } else {
          return state;
        }
      };
    }


    for (final interceptor in interceptors) {
      final fun = interceptor is QueuedInterceptor
          ? interceptor._handleRequest
          : interceptor.onRequest;
      future = future.then(requestInterceptorWrapper(fun));
    }

这个就是典型的业务添加Interceptor后的处理逻辑,当连接发生后,生成requestInterceptorHandler行为令牌,并通过requestInterceptorWrapper方法触发Interceptor的onRequest处理并传入相应的行为处理器,通过future.then实现Interceptors的链式调用。注意requestInterceptorWrapper处理了链式流转的行为。
DioMixin也通过Interceptor的机制实现包括响应解析,证书校验这两个过程。

简单设计图

架构分析

InterceptorState

处理器结果类型定义,与Completer配合使用,实现异步处理结果的返回,用于控制拦截器链式调用的结果和流转行为。T控制不同时机流转使用的数据类型,state.type定义流转行为例如在请求过程拦截器中,next向下流转,T是RequestOptions, resolve完结流转,T是Response, reject,T是DioException,该数据类型与所处的时机和流转所需的result类型有关系。

enum InterceptorResultType {
  next,
  resolve,
  resolveCallFollowing,
  reject,
  rejectCallFollowing,
}

/// Used to pass state between interceptors.
/// @nodoc
class InterceptorState<T> {
  const InterceptorState(this.data, [this.type = InterceptorResultType.next]);

  final T data;
  final InterceptorResultType type;
}

Handler

_BaseHandler

抽象类,拦截器链式处理器的模板,通过Completer实现异步拦截器处理的结果链式传递,InterceptorState, T为每个过程的类型数据,type用于控制链式传递的流转(next, reject, error)。

abstract class _BaseHandler {
  final _completer = Completer<InterceptorState>();
  void Function()? _processNextInQueue;

  
  Future<InterceptorState> get future => _completer.future;

  bool get isCompleted => _completer.isCompleted;
}

RequestInterceptorHandler

继承于_BaseHandler,
协助支持onRequest的拦截器方法实现链式调用,控制流转的方法包括
next方法,向下流转,流转类型为[RequestOptions],调用此方法将[RequestOptions]传递链式调用的下一个拦截器处理方法中。

  /// Deliver the [requestOptions] to the next interceptor.
  ///
  /// Typically, the method should be called once interceptors done
  /// manipulating the [requestOptions].
  void next(RequestOptions requestOptions) {
    _completer.complete(InterceptorState<RequestOptions>(requestOptions));
    _processNextInQueue?.call();
  }

resolve方法,完结流转,当前拦截器要求结束本次请求,并返回response作为结果。触发本方法意味着队列中的其他拦截器不再拦截处理。除非[callFollowingResponseInterceptor]被设置为ture,允许队列中剩余的继续处理该请求,相应的InterceptorResultType.resolveCallFollowing.

/// Completes the request by resolves the [response] as the result.
  ///
  /// Invoking the method will make the rest of interceptors in the queue
  /// skipped to handle the request,
  /// unless [callFollowingResponseInterceptor] is true
  /// which delivers [InterceptorResultType.resolveCallFollowing]
  /// to the [InterceptorState].
  void resolve(
    Response response, [
    bool callFollowingResponseInterceptor = false,
  ]) {
    _completer.complete(
      InterceptorState<Response>(
        response,
        callFollowingResponseInterceptor
            ? InterceptorResultType.resolveCallFollowing
            : InterceptorResultType.resolve,
      ),
    );
    _processNextInQueue?.call();
  }

reject方法,拒绝流转,通过抛出异常结束本次请求过程,同样的,如果调用handler触发reject方法,队列中其他拦截器将会不再拦截处理,除非callFollowingErrorInterceptor设置为true,此时的InterceptorState.rejectCallFollowing

void reject(DioException error,
      [bool callFollowingErrorInterceptor = false]) {
    _completer.completeError(
      InterceptorState<DioException>(
        error,
        callFollowingErrorInterceptor
            ? InterceptorResultType.rejectCallFollowing
            : InterceptorResultType.reject,
      ),
      error.stackTrace,
    );
    _processNextInQueue?.call();
  }

ResponseInterceptorHandler

继承于_BaseHandler,协助支持onResponse的拦截器方法实现链式调用,控制流转的方法包括
next方法,向下流转,流转的结果类型为[response],调用此方法将[response]传递到队列中下一个拦截器方法。

/// Deliver the [response] to the next interceptor.
  ///
  /// Typically, the method should be called once interceptors done
  /// manipulating the [response].
  void next(Response response) {
    _completer.complete(
      InterceptorState<Response>(response),
    );
    _processNextInQueue?.call();
  }

resolve方法,完结流转,当前拦截器方法要求结束本次请求,并返回[response]作为结果。

void resolve(Response response) {
    _completer.complete(
      InterceptorState<Response>(
        response,
        InterceptorResultType.resolve,
      ),
    );
    _processNextInQueue?.call();
  }

reject方法,拒绝流转,当前拦截器方法拒绝流转,抛出异常结束本次request,同样的,如果reject方法被触发,队列中其他拦截器方法将不再拦截处理,除非callFollowingErrorInterceptor设置为true,此时的InterceptorState.rejectCallFollowing

void reject(DioException error,
      [bool callFollowingErrorInterceptor = false]) {
    _completer.completeError(
      InterceptorState<DioException>(
        error,
        callFollowingErrorInterceptor
            ? InterceptorResultType.rejectCallFollowing
            : InterceptorResultType.reject,
      ),
      error.stackTrace,
    );
    _processNextInQueue?.call();
  }

ErrorInterceptorHandler

继承于_BaseHandler,协助支持onError的拦截器方法实现链式调用,控制流转的方法包括:
next方法,向下流转,流转的结果类型为[DioException],调用此方法将[DioException]传递到队列中下一个拦截器方法。。

void next(DioException error) {
    _completer.completeError(
      InterceptorState<DioException>(error),
      error.stackTrace,
    );
    _processNextInQueue?.call();
  }

resolve方法,完结流转,当前拦截器方法要求结束本次请求,并返回[DioException]作为结果。

/// Completes the request by resolves the [response] as the result.
  void resolve(Response response) {
    _completer.complete(
      InterceptorState<Response>(
        response,
        InterceptorResultType.resolve,
      ),
    );
    _processNextInQueue?.call();
  }

reject方法,当前拦截器方法拒绝流转,通过抛出异常结束本次请求过程

/// Completes the request by reject with the [error] as the result.
  void reject(DioException error) {
    _completer.completeError(
      InterceptorState<DioException>(error, InterceptorResultType.reject),
      error.stackTrace,
    );
    _processNextInQueue?.call();
  }

Interceptor

拦截器通过onRequest, onResponse,onReject,实现请求过程的三种时机的处理拦截,
onRequest,在请求将要发出时调用(对应处理器类型RequestInterceptorHandler)。
onResponse,在响应将要完结时调用(对应处理器类型ResponseInterceptorHandler)。
onReject,在整个过程抛出异常时调用(对应处理器类型ErrorInterceptorHandler)。

class Interceptor {
  /// The constructor only helps sub-classes to inherit from.
  /// Do not use it directly.
  const Interceptor();

  /// Called when the request is about to be sent.
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {
    handler.next(options);
  }

  /// Called when the response is about to be resolved.
  void onResponse(
    Response response,
    ResponseInterceptorHandler handler,
  ) {
    handler.next(response);
  }

  /// Called when an exception was occurred during the request.
  void onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) {
    handler.next(err);
  }
}

InterceptorWrap

拦截器的封装,不需要继承Interceptor实现简单拦截器的封装,通过onRequest,onResponse,onError的block参数实现简单拦截器逻辑。_InterceptorWrapperMixin实现Interceptor中的onRequest,onResponse,onError对于block的封装调度。

QueuedInterceptor

这个设计用于实现并发请求的拦截器处理,利用taskQueue对多个请求产生的拦截器进行队列管理.QueuedInterceptorsWrapper是相应的封装逻辑。
参考一下例子,

  group('QueuedInterceptor', () {
    test('requests ', () async {
      String? csrfToken;
      final dio = Dio();
      int tokenRequestCounts = 0;
      // dio instance to request token
      final tokenDio = Dio();
      dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
      dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
      final myInter = MyInterceptor();
      dio.interceptors.add(myInter);
      dio.interceptors.add(
        QueuedInterceptorsWrapper(
          onRequest: (options, handler) {
            if (csrfToken == null) {
              tokenRequestCounts++;
              tokenDio.get('/token').then((d) {
                options.headers['csrfToken'] =
                    csrfToken = d.data['data']['token'] as String;
                handler.next(options);
              }).catchError((e) {
                handler.reject(e as DioException, true);
              });
            } else {
              options.headers['csrfToken'] = csrfToken;
              handler.next(options);
            }
          },
        ),
      );

      int result = 0;
      void onResult(d) {
        if (tokenRequestCounts > 0) ++result;
      }

      await Future.wait([
        dio.get('/test?tag=1').then(onResult),
        dio.get('/test?tag=2').then(onResult),
        dio.get('/test?tag=3').then(onResult)
      ]);
      expect(tokenRequestCounts, 1);
      expect(result, 3);
      expect(myInter.requestCount, predicate((int e) => e > 0));
      // The `ImplyContentTypeInterceptor` will be replaced.
      dio.interceptors[0] = myInter;
      dio.interceptors.clear();
      expect(dio.interceptors.isEmpty, true);
    });

Dio配置了QueuedInterceptor, 并发dio.get请求发起三次,这三次的拦截器的func会被加入QueuedInterceptor的TaskQueue中进行队列管理和处理。

核心逻辑就是_handleQueue

void _handleQueue<T, V extends _BaseHandler>(
    _TaskQueue<T, V> taskQueue,
    T data,
    V handler,
    void Function(T, V) callback,
  ) {
    final task = _InterceptorParams<T, V>(data, handler);
    task.handler._processNextInQueue = () {
      if (taskQueue.queue.isNotEmpty) {
        final next = taskQueue.queue.removeFirst();
        assert(next.handler._processNextInQueue != null);
        callback(next.data, next.handler);
      } else {
        taskQueue.processing = false;
      }
    };
    taskQueue.queue.add(task);
    if (!taskQueue.processing) {
      taskQueue.processing = true;
      final task = taskQueue.queue.removeFirst();
      try {
        callback(task.data, task.handler);
      } catch (e) {
        task.handler._processNextInQueue!();
      }
    }
  }

_processNextInQueue可以认为是每个请求都会生成拦截器Iterator行为实例,这种实例是以callback(拦截器行为方法) <data, handle>(每个请求相关的数据和令牌)体现,将它们放入队列(reqQ, respQ, errQ)进行统一调度,这就是QueueInterceptor的设计的目的

队列监听器图示

Interceptors

提供一个列表管理类实现内置的Interceptor和用户级Interceptor的添加插入清理等逻辑。

Interceptors get interceptors => _interceptors;
  final Interceptors _interceptors = Interceptors();

看一下Interceptors的定义,会发现内置的Interceptor(ImplyContentTypeInterceptor)

class Interceptors extends ListMixin<Interceptor> {
  /// Define a nullable list to be capable with growable elements.
  final List<Interceptor?> _list = [const ImplyContentTypeInterceptor()];

ImplyContentTypeInterceptor只提供OnRequest,对RequestOption进行处理,option.contentType

@override
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {
    final Object? data = options.data;
    if (data != null && options.contentType == null) {
      final String? contentType;
      if (data is FormData) {
        contentType = Headers.multipartFormDataContentType;
      } else if (data is List<Map> || data is Map || data is String) {
        contentType = Headers.jsonContentType;
      } else {
        debugLog(
          '${data.runtimeType} cannot be used '
          'to imply a default content-type, '
          'please set a proper content-type in the request.',
          StackTrace.current,
        );
        contentType = null;
      }
      options.contentType = contentType;
    }
    handler.next(options);
  }
  • 25
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值