CacheControl
CacheControl
类用于表示 HTTP 请求和响应中的 Cache-Control
头部的子集信息。Cache-Control
是一个用于指定缓存策略的 HTTP 头部字段,它可以包含多个指令,如 max-age
、no-cache
、no-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
: 表示缓存的隐私属性,可以是public
或private
。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
。
方法内部的步骤如下:
- 使用
deserializeContent
方法将缓存响应的content
字节序列反序列化为指定的响应数据类型(通过options.responseType
指定)。 - 创建一个
Map
,将缓存键和fromNetwork
标志添加到extra
字段中,以便在Response
对象的extra
属性中存储这些附加信息。 - 使用
getHeaders
方法获取缓存响应的头部信息,以便在构造Response
对象时设置头部。 - 设置响应状态码为 304,表示资源没有被修改。
- 构造并返回一个
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
字段的值。如果maxStale
为null
,则设置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
方法,传入options
和content
字段(即响应体内容),以解密响应体内容。 - 调用
CacheCipher.decryptContent
方法,传入options
和headers
字段(即响应头),以解密响应头内容。 - 调用当前
CacheResponse
实例的copyWith
方法,创建一个新的CacheResponse
实例,其中的content
和headers
字段已经被解密后的内容替代。 - 返回新创建的
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
实例,其中的content
和headers
字段已经被加密后的内容替代。 - 返回新创建的
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
实例某些字段值的情况下,不必手动逐个字段地进行赋值,而是通过传入参数来创建一个新的实例。