官方描述
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);
}