DioCacheInterceptor
ini
class DioCacheInterceptor extends Interceptor {
static const String _getMethodName = 'GET';
static const String _postMethodName = 'POST';
final CacheOptions _options;
final CacheStore _store;
DioCacheInterceptor({required CacheOptions options})
: assert(options.store != null),
_options = options,
_store = options.store!;
}
DioCacheInterceptor
类是一个 Dio
拦截器,用于处理缓存逻辑。以下是该类的一部分代码:
_getMethodName
和_postMethodName
:这两个常量分别表示 HTTP 的 GET 和 POST 请求方法名。_options
:缓存选项,用于配置缓存行为。_store
:缓存存储,用于实际存储和检索缓存数据。
构造函数接受一个 CacheOptions
参数,该参数包含缓存的配置选项。assert
语句用于确保传入的选项包含一个非空的存储(store
)。
DioCacheInterceptor
类是一个拦截器,可以用于 Dio
的请求和响应过程中。它将缓存逻辑添加到请求和响应的处理过程中。
onRequest
scss
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
// Add time when the request has been sent
// for further expiry calculation.
options.extra[CacheResponse.requestSentDate] = DateTime.now();
final cacheOptions = _getCacheOptions(options);
if (_shouldSkip(options, options: cacheOptions)) {
handler.next(options);
return;
}
// Early ends if policy does not require cache lookup.
final policy = cacheOptions.policy;
if (policy != CachePolicy.request && policy != CachePolicy.forceCache) {
/// request、forceCache这两种策略, 需要缓存
handler.next(options);
return;
}
final strategy = await CacheStrategyFactory(
request: options,
cacheResponse: await _loadResponse(options),
cacheOptions: cacheOptions,
).compute();
var cacheResponse = strategy.cacheResponse;
if (cacheResponse != null) {
/// 命中缓存, 更新缓存的时间, 并将缓存返回
// Cache hit
// Update cached response if needed
cacheResponse = await _updateCacheResponse(cacheResponse, cacheOptions);
/// 将缓存的结果response返回
handler.resolve(
cacheResponse.toResponse(options, fromNetwork: false),
true,
);
return;
}
// Requests with conditional request if available
// or requests with given options
handler.next(strategy.request ?? options);
}
这段代码是 DioCacheInterceptor
类的 onRequest
方法的实现。
在这个方法中,首先记录请求发送的时间,以便后续用于计算缓存的过期时间。然后获取缓存配置选项 cacheOptions
。
接下来,通过 _shouldSkip
方法判断是否应该跳过缓存逻辑。如果应该跳过,则直接调用 handler.next(options)
将请求传递给下一个拦截器或者发送到服务器。
然后,根据缓存策略判断是否需要进行缓存查询。如果策略是 CachePolicy.request
或 CachePolicy.forceCache
,则继续执行,否则直接将请求传递给下一个拦截器或发送到服务器。
接着,通过 CacheStrategyFactory
的 compute
方法计算缓存策略。如果存在缓存的响应,则将缓存的响应进行更新(如果需要的话),然后将该响应作为结果返回,表示命中了缓存。
如果没有命中缓存,那么根据计算得到的缓存策略来确定应该如何继续处理请求。如果需要进行条件请求,会将条件请求的设置应用到请求中,或者直接传递原始请求。
总体来说,onRequest
方法实现了缓存策略的判断与处理,以及对请求的处理流程进行控制。
onResponse
scss
@override
void onResponse(
Response response,
ResponseInterceptorHandler handler,
) async {
final cacheOptions = _getCacheOptions(response.requestOptions);
if (_shouldSkip(
response.requestOptions,
response: response,
options: cacheOptions,
)) {
handler.next(response);
return;
}
/// 不缓存
if (cacheOptions.policy == CachePolicy.noCache) {
// Delete previous potential cached response
await _getCacheStore(cacheOptions).delete(
cacheOptions.keyBuilder(response.requestOptions),
);
}
/// 缓存结果response
await _saveResponse(
response,
cacheOptions,
statusCode: response.statusCode,
);
handler.next(response);
}
这段代码是 DioCacheInterceptor
类的 onResponse
方法的实现。
在这个方法中,首先获取缓存配置选项 cacheOptions
。
然后,通过 _shouldSkip
方法判断是否应该跳过缓存逻辑。如果应该跳过,则直接调用 handler.next(response)
将响应传递给下一个拦截器或者返回给调用方。
接下来,根据缓存策略判断是否需要进行缓存。如果策略是 CachePolicy.noCache
,表示不需要缓存,此时会删除之前可能存在的与该请求相关的缓存响应。
如果需要进行缓存,通过 _saveResponse
方法将响应保存到缓存中,同时将响应的状态码作为参数传递。
最后,调用 handler.next(response)
将响应传递给下一个拦截器或者返回给调用方。
这段代码实现了对响应的缓存操作,根据缓存策略和条件来决定是否进行缓存,以及是否删除之前的缓存响应。
onError
scss
@override
void onError(
DioError err,
ErrorInterceptorHandler handler,
) async {
final cacheOptions = _getCacheOptions(err.requestOptions);
if (_shouldSkip(err.requestOptions, options: cacheOptions, error: err)) {
handler.next(err);
return;
}
if (_isCacheCheckAllowed(err.response, cacheOptions)) {
// Retrieve response from cache
final existing = await _loadResponse(err.requestOptions);
// Transform CacheResponse to Response object
final cacheResponse = existing?.toResponse(err.requestOptions);
if (err.response != null && cacheResponse != null) {
// Update cache response with response header values
await _saveResponse(
cacheResponse..updateCacheHeaders(err.response),
cacheOptions,
statusCode: err.response?.statusCode,
);
}
// Resolve with found cached response
if (cacheResponse != null) {
handler.resolve(cacheResponse);
return;
}
}
handler.next(err);
}
这段代码是 DioCacheInterceptor
类的 onError
方法的实现。
在这个方法中,首先获取缓存配置选项 cacheOptions
。
然后,通过 _shouldSkip
方法判断是否应该跳过缓存逻辑。如果应该跳过,则直接调用 handler.next(err)
将错误传递给下一个拦截器或者返回给调用方。
接下来,通过 _isCacheCheckAllowed
方法判断是否允许检查缓存,这通常是在网络请求出错的情况下,会尝试从缓存中获取响应。
如果允许检查缓存,会通过 _loadResponse
方法尝试从缓存中加载之前保存的响应,然后将这个缓存响应转换为 Response
对象。
如果出现错误并且存在缓存响应,会将缓存响应的头部信息更新为错误响应的头部,并通过 _saveResponse
方法将缓存响应重新保存到缓存中。
最后,如果存在缓存响应,则调用 handler.resolve(cacheResponse)
将缓存响应返回给调用方,否则继续调用 handler.next(err)
将错误传递给下一个拦截器或者返回给调用方。
这段代码实现了在发生错误时尝试从缓存中获取响应,并将缓存响应与错误响应的头部信息进行合并。
_getCacheOptions
javascript
/// Gets cache options from given [request]
/// or defaults to interceptor options.
CacheOptions _getCacheOptions(RequestOptions request) {
return CacheOptions.fromExtra(request) ?? _options;
}
这段代码是 _getCacheOptions
方法的实现,用于获取缓存选项。
首先,它尝试从请求的额外信息中获取缓存选项,通过调用 CacheOptions.fromExtra(request)
方法来实现。如果在请求的额外信息中找不到缓存选项,就会使用构造 DioCacheInterceptor
时传递的 _options
作为默认的缓存选项。
这样,方法会返回一个有效的缓存选项,供后续的缓存逻辑使用。
_getCacheStore
javascript
/// Gets cache store from given [options]
/// or defaults to interceptor store.
CacheStore _getCacheStore(CacheOptions options) {
return options.store ?? _store;
}
这段代码是 _getCacheStore
方法的实现,用于获取缓存存储。
首先,它尝试从缓存选项中获取缓存存储,通过调用 options.store
来实现。如果缓存选项中没有指定缓存存储,就会使用构造 DioCacheInterceptor
时传递的 _store
作为默认的缓存存储。
这样,方法会返回一个有效的缓存存储,供后续的缓存逻辑使用。
_shouldSkip
kotlin
/// Check if the callback should not be proceed against HTTP method
/// or cancel error type.
bool _shouldSkip(
RequestOptions? request, {
required CacheOptions options,
Response? response,
DioError? error,
}) {
if (error?.type == DioErrorType.cancel) {
return true;
}
if (response?.extra[CacheResponse.cacheKey] != null) {
return true;
}
final rqMethod = request?.method.toUpperCase();
var result = (rqMethod != _getMethodName);
result &= (!options.allowPostMethod || rqMethod != _postMethodName);
return result;
}
这段代码是 _shouldSkip
方法的实现,用于判断是否应该跳过特定情况的回调处理。
首先,它会检查是否有错误并且错误类型是取消类型,如果是的话,就会返回 true
,表示应该跳过回调处理。
然后,它检查响应(response)是否有与缓存相关的标识。如果响应中包含缓存键(cacheKey),则也会返回 true
,表示应该跳过回调处理。
接下来,它会获取请求的 HTTP 方法,并将其转换为大写。然后,根据一些条件判断是否应该跳过回调处理。具体来说,如果请求方法不是 GET 方法,或者 options.allowPostMethod
为 false
且请求方法不是 POST 方法,那么就会返回 true
,表示应该跳过回调处理。
最终,这个方法会根据上述条件来判断是否应该跳过回调处理,返回一个布尔值。
_loadResponse
kotlin
/// Reads cached response from cache store.
Future<CacheResponse?> _loadResponse(RequestOptions request) async {
final options = _getCacheOptions(request);
final cacheKey = options.keyBuilder(request);
final cacheStore = _getCacheStore(options);
final response = await cacheStore.get(cacheKey);
if (response != null) {
// Purge entry if staled
final maxStale = CacheOptions.fromExtra(request)?.maxStale;
if ((maxStale == null || maxStale == const Duration(microseconds: 0)) &&
response.isStaled()) {
await cacheStore.delete(cacheKey);
return null;
}
/// 从缓存中读取response
return response.readContent(options);
}
return null;
}
这段代码是 _loadResponse
方法的实现,用于从缓存存储中读取已缓存的响应。
首先,它获取请求的缓存选项(CacheOptions
)和缓存键(cacheKey
)。然后,它根据缓存选项获取相应的缓存存储(cacheStore
)。接下来,它通过缓存存储的 get
方法从缓存中获取已缓存的响应(response
)。
如果获取到了已缓存的响应,接下来会检查该响应是否已过期。如果请求没有设置 maxStale
选项,或者 maxStale
的值为零,且已缓存的响应已过期,那么会将该缓存条目从缓存中移除,并返回 null
,表示该响应已过期。
最后,如果已缓存的响应未过期,就会使用 readContent
方法读取响应内容,并将其返回。
总之,这个方法的作用是从缓存存储中读取已缓存的响应,并在必要时进行一些处理,然后将已缓存的响应返回。
_saveResponse
scss
/// Writes cached response to cache store if strategy allows it.
Future<void> _saveResponse(
Response response,
CacheOptions cacheOptions, {
int? statusCode,
}) async {
final strategy = await CacheStrategyFactory(
request: response.requestOptions,
response: response,
cacheOptions: cacheOptions,
).compute();
final cacheResp = strategy.cacheResponse;
if (cacheResp != null) {
/// 缓存结果
// Store response to cache store
await _getCacheStore(cacheOptions).set(
await cacheResp.writeContent(cacheOptions, response: response),
);
// Update extra fields with cache info
response.extra[CacheResponse.cacheKey] = cacheResp.key;
response.extra[CacheResponse.fromNetwork] =
CacheStrategyFactory.allowedStatusCodes.contains(statusCode);
}
}
这段代码是 _saveResponse
方法的实现,用于将响应写入缓存存储,前提是缓存策略允许进行缓存。
首先,它使用 CacheStrategyFactory
来计算缓存策略(strategy
)。然后,它获取计算出的缓存响应(cacheResp
)。
如果获取到了缓存响应,接下来会使用缓存选项和响应来调用 writeContent
方法,将响应内容写入缓存存储。写入缓存后,会更新响应的 extra
字段,将缓存信息存储在其中,包括缓存键和是否来自网络的标识。
总之,这个方法的作用是将响应写入缓存存储中,前提是根据缓存策略允许进行缓存。写入缓存后,会更新响应的额外信息以反映缓存情况。
_isCacheCheckAllowed
kotlin
/// Checks if we can try to resolve cached response
/// against given [err] and [cacheOptions].
bool _isCacheCheckAllowed(Response? errResponse, CacheOptions cacheOptions) {
// Determine if we can return cached response
if (errResponse?.statusCode == 304) {
return true;
} else {
final hcoeExcept = cacheOptions.hitCacheOnErrorExcept;
if (hcoeExcept == null) return false;
if (errResponse == null) {
// Offline or any other connection error
return true;
} else if (!hcoeExcept.contains(errResponse.statusCode)) {
// Status code is allowed to try cache look up.
return true;
}
}
return false;
}
这段代码实现了 _isCacheCheckAllowed
方法,用于判断是否可以根据给定的错误响应(errResponse
)和缓存选项(cacheOptions
)来尝试解决缓存响应。
首先,它检查错误响应的状态码是否为 304,如果是,则表示可以返回缓存响应。
如果错误响应的状态码不是 304,它会继续检查 hitCacheOnErrorExcept
属性。如果该属性为 null
,则直接返回 false
,表示不允许尝试缓存查找。
如果错误响应不为 null
,则会检查 hcoeExcept
是否包含错误响应的状态码。如果状态码不在 hcoeExcept
中,则表示可以尝试缓存查找。
最终,根据上述条件判断,方法返回一个布尔值,表示是否允许尝试使用缓存来解决错误情况。
_updateCacheResponse
scss
/// Updates cached response if input has maxStale
/// This allows to push off deletion of the entry.
Future<CacheResponse> _updateCacheResponse(
CacheResponse cacheResponse,
CacheOptions cacheOptions,
) async {
// Add or update maxStale
final maxStaleUpdate = cacheOptions.maxStale;
if (maxStaleUpdate != null) {
cacheResponse = cacheResponse.copyWith(
maxStale: DateTime.now().toUtc().add(maxStaleUpdate),
);
// Store response to cache store
await _getCacheStore(cacheOptions).set(
await cacheResponse.writeContent(cacheOptions),
);
}
return cacheResponse;
}
这段代码实现了 _updateCacheResponse
方法,用于更新缓存响应中的 maxStale
属性。这个属性允许将缓存条目的删除推迟。
首先,它从 cacheOptions
中获取 maxStale
的更新值。如果存在更新值,它会使用当前时间加上更新值来更新 maxStale
属性,并使用更新后的缓存响应调用 copyWith
方法。
然后,它将更新后的缓存响应存储到缓存存储中,以确保更新的 maxStale
值被持久化。
最终,方法返回更新后的缓存响应。这个过程允许推迟缓存条目的删除,以便在一段时间内继续使用缓存。