NSURLCache 和 NSURLSession 不尊重:Cache-Control: max-age:86000, private, Must-revalidate

2023-12-19

在 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标头输出并返回 HTTP 304。

在我看来 NSURLCache 正在部分工作。它可以缓存响应,但它不尊重RFC 2616Apple 文档描述的语义here https://developer.apple.com/documentation/foundation/nsurlrequestcachepolicy/nsurlrequestuseprotocolcachepolicy?language=objc。仅供参考,我没有更改缓存策略,因此它使用默认值NSURLRequestUseProtocolCachePolicy.

我用谷歌搜索了一天多的时间来寻找类似的问题和其他经历过类似的问题,但我还没有找到任何解决方案。有些人在 AFNetworking 的 github issues 中询问了同样的问题,但作者关闭了该问题,因为它与 AFNetworking 没有直接关系here https://github.com/AFNetworking/AFNetworking/issues/2248 and here https://github.com/AFNetworking/AFNetworking/issues/2325.

此外,各种相关的 stackoverflow 帖子也对我没有帮助。


Problem

问题是使用了 Cache-Control 响应指令 Must-revalidate。

据我所知,通过省略必须重新验证,您已经拥有了用例的完美定义:

Cache-Control: max-age=86400, private

这控制所请求的资源被视为新鲜的时间。经过这段时间后,答案不应再直接来自缓存,而是应联系服务器以验证后续请求。在您的情况下,由于服务器提供 ETag,iOS 会向服务器发送带有 If-None-Match 标头的请求。

确认

为了检查这一点,我使用了没有 NSURLCache 设置的 testRestfulAPI 方法,并在服务器端配置了 60 秒的最大期限,因此我不必等待一天来检查结果。

之后,我每秒触发一次 testRestfulAPI。我总是从缓存中得到想要的结果。 Charles 表明数据必须来自缓存,因为 60 秒内没有联系服务器。

RFC 7234

以下是 RFC 7234(已废弃 RFC 2616)的引用,位于 5.2.2.1 下。它指出:

必须重新验证指令对于支持可靠的 某些协议功能的操作。在所有情况下都有缓存 必须遵守必须重新验证指令;特别是,如果缓存 由于任何原因无法到达源服务器,它必须生成 504 (网关超时)响应。

当且仅当服务器应该使用 Must-revalidate 指令 如果未能验证对表示的请求可能会导致 不正确的操作,例如默默地未执行的财务 交易。

阅读完本文后,如果您将自己置于缓存开发人员的角度,您可以很好地想象,当看到必须重新验证时,始终会联系原始服务器,并且任何其他指令(例如 max-age)都会被忽略。在我看来,缓存在实践中经常表现出这种行为。

5.2.2.1 章中还有一节。我不会隐瞒,内容如下:

“must-revalidate”响应指令表明,一旦它变得过时,缓存不得使用该响应来满足后续请求,而无需在源服务器上成功验证。

这通常被解释为通过指定 max-age 和 Must-revalidate ,您可以确定内容何时过时(在 max-age 秒之后),然后它必须在源服务器上进行验证,然后才能提供内容。

然而,在实践中,由于上述原因,必须重新验证似乎总是会导致对源服务器上的每个请求进行验证。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NSURLCache 和 NSURLSession 不尊重:Cache-Control: max-age:86000, private, Must-revalidate 的相关文章

随机推荐