dio_cache_interceptor缓存拦截器框架之DioCacheInterceptor源码分析(六)

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.requestCachePolicy.forceCache,则继续执行,否则直接将请求传递给下一个拦截器或发送到服务器。

接着,通过 CacheStrategyFactorycompute 方法计算缓存策略。如果存在缓存的响应,则将缓存的响应进行更新(如果需要的话),然后将该响应作为结果返回,表示命中了缓存。

如果没有命中缓存,那么根据计算得到的缓存策略来确定应该如何继续处理请求。如果需要进行条件请求,会将条件请求的设置应用到请求中,或者直接传递原始请求。

总体来说,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.allowPostMethodfalse 且请求方法不是 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 值被持久化。

最终,方法返回更新后的缓存响应。这个过程允许推迟缓存条目的删除,以便在一段时间内继续使用缓存。

相关推荐
下雪天的夏风11 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom22 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
Hello-Mr.Wang39 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步7 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者7 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋8 小时前
AJAX 入门 day1
前端·javascript·ajax