dio_cache_interceptor缓存拦截器框架之CacheControl、CacheResponse源码分析(四)

CacheControl

CacheControl 类用于表示 HTTP 请求和响应中的 Cache-Control 头部的子集信息。Cache-Control 是一个用于指定缓存策略的 HTTP 头部字段,它可以包含多个指令,如 max-ageno-cacheno-store 等,用于控制缓存的行为。

这里的 CacheControl 类可能是为了从缓存响应中解析出 Cache-Control 头部的信息,或者用于构建新的 Cache-Control 头部,以便于在网络请求和响应中进行缓存控制。

ini 复制代码
const _maxAgeHeader = 'max-age';
const _maxStaleHeader = 'max-stale';
const _minFreshHeader = 'min-fresh';
const _mustRevalidateHeader = 'must-revalidate';
const _privateHeader = 'private';
const _publicHeader = 'public';
const _noCacheHeader = 'no-cache';
const _noStoreHeader = 'no-store';
scss 复制代码
/// Cache-Control header subset representation
class CacheControl {
  /// How long the response can be used from the time it was requested (in seconds).
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2.8
  final int maxAge;

  /// 'public' / 'private'.
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2.5
  final String? privacy;

  /// Must first submit a validation request to an origin server.
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2.2
  final bool noCache;

  /// Disallow cache, overriding any other directives (Etag, Last-Modified)
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2.3
  final bool noStore;

  /// The "max-stale" request directive indicates that the client is
  /// willing to accept a response that has exceeded its freshness
  /// lifetime.
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.1.2
  final int maxStale;

  /// The "min-fresh" request directive indicates that the client is
  /// willing to accept a response whose freshness lifetime is no less than
  /// its current age.
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.1.3
  final int minFresh;

  /// The "must-revalidate" response directive indicates that once it has
  /// become stale, a cache MUST NOT use the response to satisfy subsequent
  /// requests without successful validation on the origin server.
  /// https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2.1
  final bool mustRevalidate;

  /// Other attributes not parsed
  final List<String> other;

  CacheControl({
    this.maxAge = -1,
    this.privacy,
    this.maxStale = -1,
    this.minFresh = -1,
    this.mustRevalidate = false,
    this.noCache = false,
    this.noStore = false,
    this.other = const [],
  });

  /// Builds Cache Control from header values
  factory CacheControl.fromHeader(List<String>? headerValues) {
    // Parses single header value
    void parseHeaderValue(
      StringScanner scanner,
      Map<String, String> parameters,
      List<String> other,
    ) {
      scanner.scan(whitespace);
      scanner.expect(token);

      final attribute = scanner.lastMatch![0]!;

      if (_knownAttributes.hasMatch(attribute)) {
        if (scanner.scan('=')) {
          scanner.expect(token);
          parameters[attribute] = scanner.lastMatch![0]!;
        } else {
          parameters[attribute] = attribute;
        }
      } else {
        if (scanner.scan('=')) {
          scanner.expect(token);
          other.add('$attribute=${scanner.lastMatch![0]!}');
        } else {
          other.add(attribute);
        }
      }
    }

    headerValues ??= [];

    final parameters = <String, String>{};
    final other = <String>[];

    for (var value in headerValues) {
      if (value.isNotEmpty) {
        final scanner = StringScanner(value);
        parseHeaderValue(scanner, parameters, other);

        while (scanner.scan(',')) {
          parseHeaderValue(scanner, parameters, other);
        }
        scanner.expectDone();
      }
    }

    return CacheControl(
      maxAge: int.tryParse(parameters[_maxAgeHeader] ?? '') ?? -1,
      maxStale: int.tryParse(parameters[_maxStaleHeader] ?? '') ?? -1,
      minFresh: int.tryParse(parameters[_minFreshHeader] ?? '') ?? -1,
      mustRevalidate: parameters.containsKey(_mustRevalidateHeader),
      privacy: parameters[_publicHeader] ?? parameters[_privateHeader],
      noCache: parameters.containsKey(_noCacheHeader),
      noStore: parameters.containsKey(_noStoreHeader),
      other: other,
    );
  }

  /// Serialize cache-control values
  String toHeader() {
    final header = <String>[];

    if (maxAge != -1) header.add('$_maxAgeHeader=$maxAge');
    if (maxStale != -1) header.add('$_maxStaleHeader=$maxStale');
    if (minFresh != -1) header.add('$_minFreshHeader=$minFresh');
    if (mustRevalidate) header.add(_mustRevalidateHeader);
    if (privacy != null) header.add(privacy!);
    if (noCache) header.add(_noCacheHeader);
    if (noStore) header.add(_noStoreHeader);
    if (other.isNotEmpty) header.addAll(other);

    return header.join(', ');
  }
}

上述代码定义了一个 CacheControl 类,用于表示 Cache-Control 头部的子集信息,包括各种缓存策略指令。这个类的属性和方法如下:

  • maxAge: 表示从请求发出到响应过期的时间间隔,单位为秒。
  • privacy: 表示缓存的隐私属性,可以是 publicprivate
  • noCache: 表示是否禁止使用缓存,通常用于强制服务器验证缓存的有效性。
  • noStore: 表示是否禁止存储缓存。
  • maxStale: 表示客户端愿意接受已经过期的响应的最大时间间隔,单位为秒。
  • minFresh: 表示客户端要求响应的新鲜度至少保持在给定的时间间隔内,单位为秒。
  • mustRevalidate: 表示是否必须在响应变为陈旧后,对源服务器进行验证后才能使用缓存的响应。
  • other: 其他未解析的属性列表。

除了上述属性,还有一个 CacheControl.fromHeader 构造方法,用于从 Cache-Control 头部的值解析出 CacheControl 实例。另外,还有一个 toHeader 方法,用于将 CacheControl 实例序列化为 Cache-Control 头部的字符串表示。

这个类的设计使得在处理缓存策略时更加方便,可以根据需要从头部值解析出实例,或者将实例转化为头部值。这在网络请求和响应中对缓存策略进行处理时非常有用。

CacheResponse

kotlin 复制代码
/// Response representation from cache store.
class CacheResponse {

/// Cache key available in [Response]
static const String cacheKey = '@cache_key@';

/// Available in [Response] if coming from network.
static const String fromNetwork = '@fromNetwork@';

/// Available in [RequestOptions] to know when request has been sent.
static const String requestSentDate = '@requestSentDate@';

/// Response Cache-control header
final CacheControl cacheControl;

/// Response body
List<int>? content;

/// Response Date header
final DateTime? date;

/// ETag header
final String? eTag;

/// Expires header
final DateTime? expires;

/// Response headers
List<int>? headers;

/// Key used by store
final String key;

/// Last-modified header
final String? lastModified;

/// Max stale expiry
final DateTime? maxStale;

/// Cache priority
final CachePriority priority;

/// Absolute date representing date/time when request has been sent
final DateTime requestDate;

/// Absolute date representing date/time when response has been received
final DateTime responseDate;

/// Initial request URL
final String url;

CacheResponse({
  required this.cacheControl,
  required this.content,
  required this.date,
  required this.eTag,
  required this.expires,
  required this.headers,
  required this.key,
  required this.lastModified,
  required this.maxStale,
  required this.priority,
  required this.requestDate,
  required this.responseDate,
  required this.url,
});

}

这段代码定义了一个 CacheResponse 类,用于表示缓存存储中的响应信息。以下是该类的属性和含义:

  • cacheControl:响应的 Cache-Control 头部信息,类型为 CacheControl 对象。
  • content:响应的实际内容,是一个字节序列,类型为 List<int>?
  • date:响应的 Date 头部信息,表示响应的日期,类型为 DateTime?
  • eTag:响应的 ETag 头部信息,表示实体标签,类型为 String?
  • expires:响应的 Expires 头部信息,表示过期时间,类型为 DateTime?
  • headers:响应的头部信息,是一个字节序列,类型为 List<int>?
  • key:在缓存存储中使用的键,类型为 String
  • lastModified:响应的 Last-Modified 头部信息,表示最后修改时间,类型为 String?
  • maxStale:最大陈旧时间,类型为 DateTime?
  • priority:缓存优先级,类型为 CachePriority 枚举。
  • requestDate:请求的绝对日期/时间,类型为 DateTime
  • responseDate:响应的绝对日期/时间,类型为 DateTime
  • url:初始请求的 URL,类型为 String

这些属性用于存储缓存响应的各个方面的信息,使其在后续需要时能够从缓存中获取并使用。

toResponse

php 复制代码
Response toResponse(RequestOptions options, {bool fromNetwork = false}) {
  return Response(
    data: deserializeContent(options.responseType, content),
    extra: {cacheKey: key, CacheResponse.fromNetwork: fromNetwork},
    headers: getHeaders(),
    statusCode: 304,
    requestOptions: options,
  );
}

这段代码定义了一个方法 toResponse,用于将 CacheResponse 转换为 Response 对象,以便在需要时返回缓存的响应。以下是该方法的工作原理:

  • options:请求选项,包含请求的各种配置和参数。
  • fromNetwork:一个布尔值,表示该响应是否来自网络请求,默认为 false

方法内部的步骤如下:

  1. 使用 deserializeContent 方法将缓存响应的 content 字节序列反序列化为指定的响应数据类型(通过 options.responseType 指定)。
  2. 创建一个 Map,将缓存键和 fromNetwork 标志添加到 extra 字段中,以便在 Response 对象的 extra 属性中存储这些附加信息。
  3. 使用 getHeaders 方法获取缓存响应的头部信息,以便在构造 Response 对象时设置头部。
  4. 设置响应状态码为 304,表示资源没有被修改。
  5. 构造并返回一个 Response 对象,其中包含了从缓存响应中提取的各种信息,包括反序列化后的数据、头部信息、状态码等。

通过这个方法,你可以将缓存的 CacheResponse 对象转换为适用于 Dio 库的 Response 对象,从而能够在需要时返回缓存的响应数据。

getHeaders

ini 复制代码
Headers getHeaders() {
  final checkedHeaders = headers;
  final h = Headers();

  if (checkedHeaders != null) {
    final map = jsonDecode(utf8.decode(checkedHeaders));
    map.forEach((key, value) => h.set(key, value));
  }

  return h;
}

这段代码定义了一个方法 getHeaders,用于从缓存响应中获取头部信息并返回一个 Headers 对象。以下是该方法的工作原理:

  • 首先,检查 headers 字段是否为空。
  • 创建一个空的 Headers 对象 h,用于存储头部信息。
  • 如果 headers 不为空,那么将它的字节序列转换为字符串,然后使用 UTF-8 解码。
  • 解码后的字符串是一个 JSON 格式的对象,将其解析为 Map
  • 遍历解析后的 Map,将每个键值对添加到 Headers 对象中,以准备构建最终的头部信息。
  • 返回构建完成的 Headers 对象,其中包含了从缓存响应中提取的头部信息。

总之,这个方法可以帮助你从缓存响应的 headers 字段解析出头部信息,并将其构建为一个 Dio 库中的 Headers 对象,以便在构造 Response 对象时设置响应的头部。

isStaled

csharp 复制代码
/// Checks if response is staled from [maxStale] option.
bool isStaled() {
  return maxStale?.isBefore(DateTime.now()) ?? false;
}

这段代码定义了一个方法 isStaled,用于检查响应是否已经过期(staled)基于给定的 maxStale 选项。以下是该方法的工作原理:

  • 首先,检查是否存在 maxStale,如果不存在,则返回 false,表示响应不过期。
  • 获取当前的日期和时间 DateTime.now()
  • 使用可选的 maxStale 与当前日期时间进行比较,判断是否过期。
  • 如果 maxStale 早于当前日期时间(过期),则返回 true,否则返回 false

总之,这个方法可以帮助你确定响应是否已经过期,根据缓存策略中的 maxStale 选项来判断。

isExpired

ini 复制代码
/// Checks if response is expired.
bool isExpired(CacheControl rqCacheCtrl) {
  final respCacheCtrl = cacheControl;

  final ageMillis = _cacheResponseAge();

  var freshMillis = _computeFreshnessLifetime();
  final maxAge = rqCacheCtrl.maxAge;
  if (maxAge != -1) {
    freshMillis = min(freshMillis, maxAge * 1000);
  }

  var maxStaleMillis = 0;
  final maxStale = rqCacheCtrl.maxStale;
  if (!respCacheCtrl.mustRevalidate && maxStale != -1) {
    maxStaleMillis = maxStale * 1000;
  }

  var minFreshMillis = 0;
  final minFresh = rqCacheCtrl.minFresh;
  if (minFresh != -1) {
    minFreshMillis = minFresh * 1000;
  }

  if (ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
    return false;
  }

  return true;
}

这段代码定义了一个名为 isExpired 的方法,用于检查响应是否已经过期。它会根据缓存控制(CacheControl)信息和一些计算来判断响应是否已经过期。以下是该方法的工作原理:

  • 首先,获取请求的缓存控制信息 rqCacheCtrl 和响应的缓存控制信息 respCacheCtrl
  • 计算响应的年龄(age)以毫秒为单位,使用 _cacheResponseAge 方法计算。
  • 计算响应的新鲜度寿命(freshness lifetime)以毫秒为单位,使用 _computeFreshnessLifetime 方法计算。
  • 如果请求的缓存控制中设置了 maxAge,则使用该值更新新鲜度寿命。
  • 如果响应的缓存控制中设置了 maxStale 且不需要强制重新验证,则使用该值计算最大过期时间(max stale)。
  • 如果请求的缓存控制中设置了 minFresh,则使用该值计算最小新鲜度寿命。
  • 最后,通过比较响应的年龄和新鲜度寿命以及最大过期时间和最小新鲜度寿命来判断响应是否已过期。如果年龄加上最小新鲜度寿命小于等于新鲜度寿命加上最大过期时间,则表示响应未过期,返回 false;否则表示响应已过期,返回 true

总之,这个方法会根据缓存控制和计算来判断响应是否已经过期。

_cacheResponseAge

ini 复制代码
/// Calculating Age.
/// https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.3
int _cacheResponseAge() {
  final nowMillis = DateTime.now().millisecondsSinceEpoch;
  final servedDate = date;
  final sentRequestMillis = requestDate.millisecondsSinceEpoch;
  final receivedResponseMillis = responseDate.millisecondsSinceEpoch;

  final headers = getHeaders();
  final ageSeconds = int.tryParse(headers[ageHeader]?.first ?? '') ?? -1;

  final apparentReceivedAge = (servedDate != null)
      ? max(0, receivedResponseMillis - servedDate.millisecondsSinceEpoch)
      : 0;

  final receivedAge = (ageSeconds > -1)
      ? max(apparentReceivedAge, ageSeconds * 1000)
      : apparentReceivedAge;

  final responseDuration = receivedResponseMillis - sentRequestMillis;
  final residentDuration = nowMillis - receivedResponseMillis;

  return receivedAge + responseDuration + residentDuration;
}

这段代码实现了计算缓存响应的年龄(age)的逻辑。根据 HTTP 协议的规范(RFC 7234),年龄是一个指示响应在代理服务器上存在的时间的值。以下是该方法的工作原理:

  • 获取当前时间的毫秒数(nowMillis)。
  • 获取服务器提供的日期(servedDate)。
  • 获取请求发送时间(sentRequestMillis)和响应接收时间(receivedResponseMillis)的毫秒数。
  • 从响应的头部获取 age 字段的值,即响应的年龄(以秒为单位)。
  • 计算表面接收年龄(apparentReceivedAge):如果服务器提供了日期,就计算响应接收时间与服务器提供日期之间的差值,否则为 0。
  • 计算实际接收年龄(receivedAge):如果 age 字段的值有效,则取表面接收年龄和 age 字段值乘以 1000 的较大值,否则取表面接收年龄。
  • 计算响应持续时间(responseDuration):计算响应接收时间与请求发送时间之间的差值。
  • 计算驻留持续时间(residentDuration):计算当前时间与响应接收时间之间的差值。
  • 返回实际接收年龄加上响应持续时间和驻留持续时间的值,即缓存响应的年龄。

总之,这个方法根据一系列时间和头部字段的值来计算缓存响应的年龄,遵循 HTTP 协议规范。

_computeFreshnessLifetime

kotlin 复制代码
/// Returns the number of milliseconds that the response was fresh for.
/// Calculating Freshness Lifetime.
/// https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.1
int _computeFreshnessLifetime() {
  final maxAge = cacheControl.maxAge;
  if (maxAge != -1) {
    return maxAge * 1000;
  }

  final checkedExpires = expires;
  if (checkedExpires != null) {
    final delta =
        checkedExpires.difference(date ?? responseDate).inMilliseconds;
    return (delta > 0) ? delta : 0;
  }

  if (lastModified != null && Uri.parse(url).query.isEmpty) {
    final sentRequestMillis = requestDate.millisecondsSinceEpoch;
    // As recommended by the HTTP RFC, the max age of a document
    // should be defaulted to 10% of the document's age
    // at the time it was served.
    // Default expiration dates aren't used for URIs containing a query.
    final servedMillis = date?.millisecondsSinceEpoch ?? sentRequestMillis;
    final delta =
        servedMillis - HttpDate.parse(lastModified!).millisecondsSinceEpoch;
    return ((delta > 0) ? delta / 10 : 0).round();
  }

  return 0;
}

这段代码实现了计算响应的新鲜度寿命(freshness lifetime)的逻辑。新鲜度寿命是指响应可以被视为"新鲜"的时间段,根据缓存控制策略和响应头中的信息计算而得。以下是该方法的工作原理:

  • 获取缓存控制中的 maxAge 值,表示响应的最大新鲜度寿命(以秒为单位)。如果该值存在且不为 -1,则返回 maxAge * 1000,将秒转换为毫秒。

  • 获取响应的过期时间(expires)。如果过期时间存在,则计算过期时间与日期头部或响应接收时间之间的时间差,并返回时间差的毫秒数。

  • 如果过期时间不存在,但存在 lastModified 响应头且 URL 中不包含查询参数,说明响应的最大新鲜度寿命可以通过一些计算得到:

    • 获取请求发送时间的毫秒数(sentRequestMillis)。
    • 获取服务器提供的日期(date)或响应接收时间的毫秒数(responseDate)作为初始值。
    • 计算服务器提供日期或响应接收时间与 lastModified 的时间差(delta)。
    • 如果时间差大于 0,返回 delta / 10 的四舍五入值作为新鲜度寿命,否则返回 0。
  • 如果以上条件均不满足,则返回 0,表示没有新鲜度寿命。

总之,这个方法根据缓存控制策略、响应头和其他时间信息来计算响应的新鲜度寿命,遵循 HTTP 协议规范

fromResponse

php 复制代码
static Future<CacheResponse> fromResponse({
  required String key,
  required CacheOptions options,
  required Response response,
}) async {
  final dateStr = response.headers[dateHeader]?.join(',');
  DateTime? date;
  if (dateStr != null) {
    try {
      date = HttpDate.parse(dateStr);
    } catch (_) {
      // Invalid date format => ignored
    }
  }

  final expiresDateStr = response.headers[expiresHeader]?.join(',');
  DateTime? httpExpiresDate;
  if (expiresDateStr != null) {
    try {
      httpExpiresDate = HttpDate.parse(expiresDateStr);
    } catch (_) {
      // Invalid date format => meaning something already expired
      httpExpiresDate = DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true);
    }
  }

  final checkedMaxStale = options.maxStale;

  return CacheResponse(
    cacheControl: CacheControl.fromHeader(
      response.headers[cacheControlHeader],
    ),
    content: null,
    date: date,
    eTag: response.headers[etagHeader]?.join(','),
    expires: httpExpiresDate,
    headers: utf8.encode(jsonEncode(response.headers.map)),
    key: key,
    lastModified: response.headers[lastModifiedHeader]?.join(','),
    maxStale: checkedMaxStale != null
        ? DateTime.now().toUtc().add(checkedMaxStale)
        : null,
    priority: options.priority,
    requestDate: response.requestOptions.extra[CacheResponse.requestSentDate],
    responseDate: DateTime.now().toUtc(),
    url: response.requestOptions.uri.toString(),
  );
}

这段代码实现了从一个 Response 对象构建 CacheResponse 对象的逻辑。它从传入的 Response 对象中提取响应头信息和其他相关数据,然后创建一个新的 CacheResponse 实例。以下是该方法的工作原理:

  • 从响应头中提取日期头部(Date)的值,并尝试将其解析为 DateTime 对象,作为 date 字段的值。

  • 从响应头中提取过期时间头部(Expires)的值,并尝试将其解析为 DateTime 对象,作为 expires 字段的值。如果解析失败,说明过期时间已过,将 httpExpiresDate 设置为 DateTime 的初始值。

  • 获取 CacheOptions 中的 maxStale 值,将其转换为 DateTime 对象,作为 maxStale 字段的值。如果 maxStalenull,则设置 maxStale 字段为 null

  • 创建一个新的 CacheControl 实例,从响应头中的缓存控制头部值解析而来。

  • 构建并返回一个新的 CacheResponse 实例,包括以下字段:

    • cacheControl: 之前创建的 CacheControl 实例。
    • content: 设置为 null,因为在这个阶段暂时没有响应体内容。
    • date: 之前解析的日期头部值。
    • eTag: 响应头中的 ETag 值。
    • expires: 之前解析的过期时间头部值。
    • headers: 将响应头映射编码为 JSON 字符串,然后转换为 UTF-8 编码的字节流。
    • key: 传入的缓存键。
    • lastModified: 响应头中的最后修改时间。
    • maxStale: 之前转换的 maxStale 值。
    • priority: 从 CacheOptions 中获取的优先级。
    • requestDate: 从响应的请求选项中获取的请求发送日期。
    • responseDate: 当前的 UTC 时间。
    • url: 请求的 URL。

总之,这个方法将一个 Response 对象转换为一个 CacheResponse 对象,并根据提取的信息创建了一个新的缓存响应实例。

readContent

csharp 复制代码
Future<CacheResponse> readContent(CacheOptions options) async {
  return copyWith(
    content: await CacheCipher.decryptContent(options, content),
    headers: await CacheCipher.decryptContent(options, headers),
  );
}

这段代码实现了从缓存中读取响应内容的逻辑。它使用 CacheCipher.decryptContent 方法对缓存的内容和响应头进行解密,然后创建一个新的 CacheResponse 实例,其中内容和响应头已经被解密。以下是该方法的工作原理:

  • 调用 CacheCipher.decryptContent 方法,传入 optionscontent 字段(即响应体内容),以解密响应体内容。
  • 调用 CacheCipher.decryptContent 方法,传入 optionsheaders 字段(即响应头),以解密响应头内容。
  • 调用当前 CacheResponse 实例的 copyWith 方法,创建一个新的 CacheResponse 实例,其中的 contentheaders 字段已经被解密后的内容替代。
  • 返回新创建的 CacheResponse 实例,其中包含解密后的内容和响应头。

这个方法的目的是从缓存中读取已经加密的响应内容和响应头,将其解密后返回一个新的 CacheResponse 实例。

writeContent

css 复制代码
Future<CacheResponse> writeContent(
  CacheOptions options, {
  Response? response,
}) async {
  if (response != null) {
    return copyWith(
      content: await CacheCipher.encryptContent(
        options,
        await serializeContent(
          response.requestOptions.responseType,
          response.data,
        ),
      ),
      headers: await CacheCipher.encryptContent(
        options,
        utf8.encode(jsonEncode(response.headers.map)),
      ),
    );
  }

  return copyWith(
    content: await CacheCipher.encryptContent(options, content),
    headers: await CacheCipher.encryptContent(options, headers),
  );
}

这段代码实现了将响应内容写入缓存的逻辑。它使用 CacheCipher.encryptContent 方法对响应内容和响应头进行加密,然后创建一个新的 CacheResponse 实例,其中内容和响应头已经被加密。以下是该方法的工作原理:

  • 如果提供了 response 参数,表示要将来自网络的响应内容写入缓存。这时,会调用 CacheCipher.encryptContent 方法,传入 options 和序列化后的响应体内容,以加密响应体内容。
  • 调用 CacheCipher.encryptContent 方法,传入 options 和序列化后的响应头,以加密响应头内容。
  • 调用当前 CacheResponse 实例的 copyWith 方法,创建一个新的 CacheResponse 实例,其中的 contentheaders 字段已经被加密后的内容替代。
  • 返回新创建的 CacheResponse 实例,其中包含加密后的内容和响应头。

这个方法的目的是将响应内容和响应头加密后存储到缓存中,以便后续读取使用。

copyWith

kotlin 复制代码
CacheResponse copyWith({
  CacheControl? cacheControl,
  List<int>? content,
  DateTime? date,
  String? eTag,
  DateTime? expires,
  List<int>? headers,
  String? key,
  String? lastModified,
  DateTime? maxStale,
  CachePriority? priority,
  DateTime? requestDate,
  DateTime? responseDate,
  String? url,
}) {
  return CacheResponse(
    cacheControl: cacheControl ?? this.cacheControl,
    content: content ?? this.content,
    date: date ?? this.date,
    eTag: eTag ?? this.eTag,
    expires: expires ?? this.expires,
    headers: headers ?? this.headers,
    key: key ?? this.key,
    lastModified: lastModified ?? this.lastModified,
    maxStale: maxStale ?? this.maxStale,
    priority: priority ?? this.priority,
    requestDate: requestDate ?? this.requestDate,
    responseDate: responseDate ?? this.responseDate,
    url: url ?? this.url,
  );
}

这段代码实现了 CacheResponse 类的 copyWith 方法。这个方法用于创建一个新的 CacheResponse 实例,通过传入的参数更新原始实例的字段值。如果某个参数没有传入,则对应的字段值将保持不变。以下是该方法的工作原理:

  • 接收多个可选参数,用于更新 CacheResponse 实例的不同字段。如果某个参数没有传入,则使用原始实例中的对应字段值。
  • 创建一个新的 CacheResponse 实例,将传入的参数或原始实例的字段值分别赋给对应的字段。
  • 返回新创建的 CacheResponse 实例,其中字段值已经根据传入参数进行了更新。

这个方法的目的是为了方便在需要修改 CacheResponse 实例某些字段值的情况下,不必手动逐个字段地进行赋值,而是通过传入参数来创建一个新的实例。

相关推荐
昱禹6 分钟前
关于CSS Grid布局
前端·javascript·css
啊QQQQQ13 分钟前
HTML:相关概念以及标签
前端·html
就叫飞六吧1 小时前
vue2和vue3全面对比
前端·javascript·vue.js
Justinc.1 小时前
CSS基础-盒子模型(三)
前端·css
qq_2518364571 小时前
基于ssm vue uniapp实现的爱心小屋公益机构智慧管理系统
前端·vue.js·uni-app
._Ha!n.1 小时前
Vue基础(二)
前端·javascript·vue.js
风清扬_jd2 小时前
Chromium 硬件加速开关c++
java·前端·c++
谢尔登3 小时前
【React】事件机制
前端·javascript·react.js
2401_857622664 小时前
SpringBoot精华:打造高效美容院管理系统
java·前端·spring boot
etsuyou4 小时前
Koa学习
服务器·前端·学习