ios - NSURLCache,连同 NSURLSession,不尊重:Cache-Control: max-age:86000, private, must-revalidate
问题描述
在 AppDelegate.m 中,我配置了:
NSURLCache *sharedURLCache = [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024 diskCapacity:100 * 1024 * 1024 diskPath:@"FhtHttpCacheDir"];
然后是http请求:
- (void) testRestfulAPI{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://192.168.0.223:8000/v1/topictypes"]];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSError *error = nil;
if (!error) {
NSURLSessionDataTask *downloadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
NSLog(@"JSON: %@", json);
}
}
}];
[downloadTask resume];
}
}
第一次请求时,它获得了带有 Etag + Cache-Control 标头的 HTTP 200。没问题。
如果我没记错的话,Cache-Control: must-revalidate, max-age=86400, private
会告诉 NSURLCache 在 24 小时内认为缓存是新鲜的,并且在接下来的 24 小时内不会进行任何网络调用。
但事实并非如此,第二次发出 http 请求时,它实际上发出了If-None-Match
headers 并返回了 HTTP 304。
在我看来, NSURLCache 部分工作。它可以缓存响应,但它不遵守Apple 文档在此处描述的RFC 2616语义。仅供参考,我没有更改缓存策略,因此它使用默认的.NSURLRequestUseProtocolCachePolicy
我用谷歌搜索了一天多的类似问题和其他经历过的类似问题,但我没有找到任何解决方案。有些人在 AFNetworking 的 github 问题中询问了同样的问题,但作者关闭了这个问题,因为它与 AFNetworking在这里和这里没有直接关系。
各种相关的stackoverflow帖子也对我没有帮助。
解决方案
问题
问题是 Cache-Control 响应指令 must-revalidate 的使用。
据我所知,通过省略 must-revalidate ,您已经对用例有了完美的定义:
Cache-Control: max-age=86400, private
这控制所请求的资源被认为是新鲜的多长时间。这段时间过去后,答案不应再直接来自缓存,而是应联系服务器以验证后续请求。在您的情况下,由于服务器提供了一个 ETag,iOS 向服务器发送了一个带有 If-None-Match 标头的请求。
确认
为了检查这一点,我使用了没有 NSURLCache 设置的 testRestfulAPI 方法,并在服务器端配置了最长 60 秒的时间,因此我不必等待一天来检查结果。
之后,我每秒触发一次 testRestfulAPI。我总是从缓存中得到想要的结果。并且查尔斯表明数据必须来自缓存,因为服务器没有联系 60 秒。
RFC 7234
这是来自 RFC 7234(已废弃 RFC 2616)的引用,位于 5.2.2.1 下。它指出:
must-revalidate 指令对于支持某些协议功能的可靠操作是必要的。在所有情况下,缓存都必须遵守 must-revalidate 指令;特别是,如果缓存因任何原因无法到达源服务器,它必须生成 504(网关超时)响应。
当且仅当未能验证对表示的请求可能导致不正确的操作(例如静默未执行的金融交易)时,服务器才应该使用 must-revalidate 指令。
读完之后,如果你站在缓存开发者的角度,你可以想象,当看到 must-revalidate 时,总是会联系原始服务器,而任何额外的指令,如 max-age 都会被忽略。在我看来,缓存在实践中经常表现出这种行为。
第 5.2.2.1 章还有另一节。我不会隐瞒,内容如下:
“must-revalidate”响应指令表明,一旦它变得陈旧,缓存不能使用响应来满足后续请求,而无需在源服务器上成功验证。
这通常被解释为通过将 max-age 与 must-revalidate 一起指定,您可以确定内容何时过时(在 max-age 秒之后),然后它必须在源服务器上验证才能提供内容。
然而,在实践中,由于上述原因,must-revalidate 似乎总是导致对源服务器上的每个请求进行验证。
推荐阅读
- ios - 填充矩形而不干扰 HStack 的几何形状
- solr - SOLR:如何忽略 MinFieldValueUpdateProcessorFactory 处理器中的 0(零)值?
- python - 从列表中创建数据框中的列(列数更改)
- node.js - 使用 JavaScript 框架和 Node.js 进行身份验证
- c# - 在 HoloLens 2 上访问 MediaFrameSourceGroup 参数的问题
- angular - 检查后表达式已更改。以前的值:''。当前值:“测试”
- javascript - currentFocusedField 已弃用,将在未来版本中使用 TextInput 道具“secureTextEntry”时删除
- reactjs - 如何将 JWT 令牌存储在 cookie React fetch 中
- android - 在 Intune 门户中打开应用程序的深度链接或动态链接
- mysql - WordPress数据库错误锁定等待超时;尝试为查询 UPDATE wp_wppa_session 重新启动事务