ImplyContentTypeInterceptor
/// {@template dio.interceptors.ImplyContentTypeInterceptor}
/// The default `content-type` for requests will be implied by the
/// [ImplyContentTypeInterceptor] according to the type of the request payload.
/// The interceptor can be removed by
/// [Interceptors.removeImplyContentTypeInterceptor].
/// {@endtemplate}
class ImplyContentTypeInterceptor extends Interceptor {
const ImplyContentTypeInterceptor();
@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 {
// Do not log in the release mode.
if (!_kReleaseMode) {
dev.log(
'${data.runtimeType} cannot be used '
'to imply a default content-type, '
'please set a proper content-type in the request.',
level: 900,
name: '🔔 Dio',
stackTrace: StackTrace.current,
);
}
contentType = null;
}
options.contentType = contentType;
}
handler.next(options);
}
}
ImplyContentTypeInterceptor 是 Dio 中的一个拦截器,它用于根据请求的有效负载类型自动推断并设置请求的默认 content-type。在请求的过程中,该拦截器会检查请求的有效负载类型,如果请求的 content-type 未设置且有效负载存在,则根据有效负载类型自动设置相应的 content-type。
该拦截器的作用是简化请求配置过程。例如,如果发送包含 JSON 数据的请求,Dio 会自动设置请求的 content-type 为 "application/json";如果发送包含表单数据的请求,Dio 会设置 content-type 为 "multipart/form-data"。
拦截器的具体实现如下:
-
首先,它获取请求中的有效负载数据
data。 -
然后,它检查
options.contentType是否为空(即是否手动设置了content-type)。如果为空且有效负载存在,则继续下一步。 -
接着,它根据有效负载类型设置相应的
content-type:- 如果有效负载是
FormData类型,则设置content-type为"multipart/form-data"。 - 如果有效负载是
List<Map>、Map或String类型,则设置content-type为"application/json"。 - 如果有效负载类型无法判断或不支持的类型,则不设置
content-type,并在开发模式下打印日志提醒用户手动设置合适的content-type。
- 如果有效负载是
-
最后,将更新后的
options继续传递给下一个拦截器或发起请求。
使用 ImplyContentTypeInterceptor 可以简化请求时设置 content-type 的步骤,尤其在发送不同类型数据的请求时,无需手动指定 content-type,拦截器会根据有效负载自动推断和设置。
Interceptors
/// Interceptors are a queue, and you can add any number of interceptors,
/// All interceptors will be executed in first in first out order.
class Interceptors extends ListMixin<Interceptor> {
/// Define a nullable list to be capable with growable elements.
final List<Interceptor?> _list = [const ImplyContentTypeInterceptor()];
@override
int get length => _list.length;
@override
set length(int newLength) {
_list.length = newLength;
}
@override
Interceptor operator [](int index) => _list[index]!;
@override
void operator []=(int index, Interceptor value) {
if (_list.length == index) {
_list.add(value);
} else {
_list[index] = value;
}
}
/// Remove the default imply content type interceptor.
void removeImplyContentTypeInterceptor() {
_list.removeWhere((e) => e is ImplyContentTypeInterceptor);
}
}
Interceptors 是 Dio 中的一个拦截器队列,用于管理多个拦截器,并确保这些拦截器按照先进先出的顺序依次执行。该类继承自 ListMixin<Interceptor>,使其具有列表的特性,可以像操作列表一样添加和删除拦截器。
该类的主要功能如下:
- 定义了一个
_list,它是一个拦截器列表,并初始化时包含了一个默认的ImplyContentTypeInterceptor拦截器。 - 通过实现
length属性和lengthsetter 方法,允许获取和设置_list的长度。 - 通过实现索引操作符
[]和索引赋值操作符[]=,使得可以像操作列表一样获取和设置拦截器。 - 提供了方法
removeImplyContentTypeInterceptor,用于移除默认的ImplyContentTypeInterceptor拦截器。 - 通过继承
ListMixin,使得Interceptors类可以像列表一样进行遍历和操作,例如使用for-in循环遍历拦截器。
使用 Interceptors 类,你可以方便地管理多个拦截器,以满足不同请求的需求。例如,你可以通过 add 方法将新的拦截器添加到队列中,然后它们会按照添加的先后顺序依次执行;也可以通过索引操作符 [] 和索引赋值操作符 []= 来修改已有的拦截器。
拦截器队列的概念是 Dio 中非常重要的一部分,它使得我们可以在请求发送和响应返回的过程中对请求和响应进行统一的处理和配置,例如设置统一的请求头、打印日志、处理错误等。
_InterceptorParams
class _InterceptorParams<T, V> {
const _InterceptorParams(this.data, this.handler);
final T data;
final V handler;
}
_InterceptorParams 是一个泛型类,它接收两个类型参数 T 和 V。该类用于封装拦截器的参数,其中 data 是泛型类型 T 的数据,handler 是泛型类型 V 的处理器。
类成员说明:
data: 用于存储拦截器所需的数据,它的类型是T。handler: 用于存储处理器,处理器的类型是V。
这个类主要用于在 Dio 拦截器队列中传递拦截器所需的数据和处理器。当创建一个拦截器时,可以使用 _InterceptorParams 将数据和处理器封装起来,并将其添加到拦截器队列中。然后在拦截器的具体实现中,可以通过访问 _InterceptorParams 中的 data 和 handler 来获取拦截器所需的数据和处理器。
这样设计可以方便地在拦截器之间传递数据,同时保持类型安全。使用泛型参数使得 _InterceptorParams 可以适用于不同类型的拦截器,而不需要针对每个拦截器都定义一个新的类。这样,拦截器的参数传递和处理变得更加灵活和可扩展。
_TaskQueue
class _TaskQueue {
final queue = Queue<_InterceptorParams>();
bool processing = false;
}
_TaskQueue 是一个私有类,用于管理拦截器任务队列。该类包含两个主要成员:
queue: 一个队列(Queue),用于存储_InterceptorParams类型的任务参数。每个任务参数封装了拦截器所需的数据和处理器。processing: 一个布尔值,表示当前队列是否正在处理任务。当processing为true时,表示当前队列正在执行拦截器任务,否则为false表示队列处于空闲状态。
类成员说明:
queue: 用于存储拦截器任务参数的队列,可以通过add方法将任务参数添加到队列中,通过removeFirst方法从队列中获取并移除第一个任务参数。processing: 用于标识队列是否正在处理任务。当开始执行队列中的任务时,将processing设置为true,任务执行完毕后再将其设置为false。
在拦截器的执行过程中,可能会有多个拦截器同时处理请求或响应。为了避免并发冲突和保证任务的顺序执行,使用 _TaskQueue 类来管理拦截器任务的调度和执行。每个拦截器的任务参数都会被添加到队列中,然后按照先进先出的顺序逐个执行。当一个任务执行完毕后,会检查队列中是否还有未处理的任务,如果有,则继续执行下一个任务,直到队列中的所有任务都被执行完毕。
通过这种方式,保证了拦截器任务的有序执行,避免了并发问题,并且可以灵活地管理拦截器任务的调度。
QueuedInterceptor
/// Serialize the request/response/error before they enter the interceptor.
///
/// If there are multiple concurrent requests, the request is added to a queue before
/// entering the interceptor. Only one request at a time enters the interceptor, and
/// after that request is processed by the interceptor, the next request will enter
/// the interceptor.
class QueuedInterceptor extends Interceptor {
final _TaskQueue _requestQueue = _TaskQueue();
final _TaskQueue _responseQueue = _TaskQueue();
final _TaskQueue _errorQueue = _TaskQueue();
void _handleRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) {
_handleQueue(_requestQueue, options, handler, onRequest);
}
void _handleResponse(
Response<dynamic> response,
ResponseInterceptorHandler handler,
) {
_handleQueue(_responseQueue, response, handler, onResponse);
}
void _handleError(
DioError err,
ErrorInterceptorHandler handler,
) {
_handleQueue(_errorQueue, err, handler, onError);
}
void _handleQueue<T, V extends _BaseHandler>(
_TaskQueue taskQueue,
T data,
V handler,
callback,
) {
final task = _InterceptorParams<T, V>(data, handler);
task.handler._processNextInQueue = _processNextTaskInQueueCallback(
taskQueue,
callback,
);
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();
}
}
}
}
QueuedInterceptor 是一个拦截器,用于串行化请求、响应和错误处理。在并发请求的情况下,该拦截器可以确保每个请求在进入拦截器之前都会被添加到一个队列中,只有一个请求在拦截器中进行处理。处理完一个请求后,才会处理下一个请求,以此保证请求的有序执行。
类成员说明:
_requestQueue: 用于存储请求任务参数的队列。_responseQueue: 用于存储响应任务参数的队列。_errorQueue: 用于存储错误任务参数的队列。_handleRequest: 用于处理请求的方法。当有新的请求进入时,会调用_handleQueue方法将其加入请求队列,并启动处理队列中的任务。_handleResponse: 用于处理响应的方法。当有新的响应进入时,会调用_handleQueue方法将其加入响应队列,并启动处理队列中的任务。_handleError: 用于处理错误的方法。当有新的错误进入时,会调用_handleQueue方法将其加入错误队列,并启动处理队列中的任务。_handleQueue: 用于处理任务队列的方法。将任务加入队列,并根据当前队列的状态决定是否执行任务。_processNextTaskInQueueCallback: 返回一个回调函数,用于在任务完成后处理下一个任务。这样可以确保在一个任务完成后,能够继续执行队列中的下一个任务。
当请求、响应或错误发生时,QueuedInterceptor 将会将其封装成 _InterceptorParams 类型的任务参数,并加入相应的队列中。然后,根据队列的状态决定是否执行任务。如果队列当前为空闲状态,将会直接执行任务;否则,等待当前任务完成后,再继续执行队列中的下一个任务。
通过使用 QueuedInterceptor,可以确保请求、响应和错误的处理按照顺序进行,避免了并发问题,提高了拦截器的稳定性和可靠性。
_processNextTaskInQueueCallback
void Function() _processNextTaskInQueueCallback(_TaskQueue taskQueue, cb) {
return () {
if (taskQueue.queue.isNotEmpty) {
final next = taskQueue.queue.removeFirst();
assert(next.handler._processNextInQueue != null);
cb(next.data, next.handler);
} else {
taskQueue.processing = false;
}
};
}
_processNextTaskInQueueCallback 是一个回调函数,用于在一个任务完成后处理下一个任务。
该函数接受两个参数:
taskQueue: 任务队列,表示要处理的任务所属的队列,可以是请求队列、响应队列或错误队列。cb: 回调函数,表示要执行的任务处理方法,可以是onRequest、onResponse或onError。
该函数返回一个无参数的闭包函数,用于处理下一个任务。在闭包函数中,首先判断任务队列是否还有任务,如果有,则从队列中移除第一个任务,并取出该任务对应的数据和处理方法。接着,通过调用传入的回调函数 cb 来执行任务处理,将任务数据和处理方法作为参数传递给回调函数。这样就实现了在一个任务完成后,自动处理队列中的下一个任务。
如果队列已经为空(没有待处理的任务),则将 taskQueue.processing 标记为 false,表示当前任务队列已处理完毕,可以接受新的任务。这样,在有新任务进入时,就可以继续启动处理任务队列。
这种方式保证了任务的有序执行,避免了并发问题,确保了请求、响应和错误处理的稳定性和正确性。
QueuedInterceptorsWrapper
/// [QueuedInterceptorsWrapper] is a helper class, which is used to conveniently
/// create [QueuedInterceptor]s.
///
/// See also:
/// - [Interceptor]
/// - [InterceptorsWrapper]
/// - [QueuedInterceptors]
class QueuedInterceptorsWrapper extends QueuedInterceptor
with _InterceptorWrapperMixin {
QueuedInterceptorsWrapper({
InterceptorSendCallback? onRequest,
InterceptorSuccessCallback? onResponse,
InterceptorErrorCallback? onError,
}) : __onRequest = onRequest,
__onResponse = onResponse,
__onError = onError;
@override
InterceptorSendCallback? get _onRequest => __onRequest;
final InterceptorSendCallback? __onRequest;
@override
InterceptorSuccessCallback? get _onResponse => __onResponse;
final InterceptorSuccessCallback? __onResponse;
@override
InterceptorErrorCallback? get _onError => __onError;
final InterceptorErrorCallback? __onError;
}
QueuedInterceptorsWrapper 是一个帮助类,用于方便地创建 QueuedInterceptor 实例。
它继承自 QueuedInterceptor,并使用了 _InterceptorWrapperMixin,通过混入 _InterceptorWrapperMixin,可以在 QueuedInterceptor 的基础上添加自定义的请求、响应和错误处理方法。
该类提供了三个参数:
onRequest: 请求处理回调,在请求被初始化之前执行。onResponse: 响应处理回调,在请求成功时执行。onError: 错误处理回调,在请求失败时执行。
在具体的处理方法中,如果对应的回调不为空,则执行该回调,否则调用基类 QueuedInterceptor 的处理方法。这样就可以根据需要,自定义拦截器的行为。
通过使用 QueuedInterceptorsWrapper,可以方便地创建具有自定义拦截器行为的 QueuedInterceptor 实例,并在多个请求中按顺序执行这些拦截器,确保请求的有序处理。