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 值被持久化。

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

相关推荐
多则惑少则明33 分钟前
Vue开发系列——自定义组件开发
前端·javascript·vue.js
用户2506949216140 分钟前
next框架打包.next文件夹部署
前端
程序猿小蒜43 分钟前
基于springboot的校园社团信息管理系统开发与设计
java·前端·spring boot·后端·spring
一叶难遮天43 分钟前
开启RN之旅——前端基础
前端·javascript·promise·js基础·es6/ts·npm/nrm
申阳44 分钟前
Day 4:02. 基于Nuxt开发博客项目-整合 Inspira UI
前端·后端·程序员
程序猿_极客1 小时前
【期末网页设计作业】HTML+CSS+JavaScript 猫咪主题网站开发(附源码与效果演示)
前端·css·html·课程设计·网页设计作业
IT古董1 小时前
【前端】从零开始搭建现代前端框架:React 19、Vite、Tailwind CSS、ShadCN UI 完整实战教程-第1章:项目概述与技术栈介绍
前端·react.js·前端框架
有点笨的蛋1 小时前
从零搭建小程序首页:新手也能看懂的结构解析与实战指南
前端·微信小程序
爱宇阳1 小时前
Vue3 前端项目 Docker 容器化部署教程
前端·docker·容器
Irene19911 小时前
前端缓存技术和使用场景
前端·缓存