NSURLCache与NSURLSession不兼容:缓存控制:最大年龄:86000,专用,必须重新验证

时间:2018-08-10 02:39:21

标签: ios nsurlsession http-caching nsurlcache

在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。没问题。

enter image description here

如果我没看错,Cache-Control: must-revalidate, max-age=86400, private会告诉NSURLCache在24小时内认为该缓存是最新的,并且在接下来的24小时内不会进行任何网络调用。

事实并非如此,第二次发出http请求时,它实际上发出了If-None-Match标头并返回了HTTP 304。

enter image description here

在我看来,NSURLCache部分起作用。它可以缓存响应,但是不遵循Apple文档对此here的描述, RFC 2616 语义。仅供参考,我没有更改缓存策略,因此它使用默认的NSURLRequestUseProtocolCachePolicy

我针对类似问题和其他经历过类似问题的问题在Google上搜索了一天多,但没有找到任何解决方案。一些人在AFNetworking的github问题中问了同样的问题,但作者关闭了该问题,因为它与AFNetworking herehere没有直接关系。

各种相关的stackoverflow帖子也没有帮助我。

2 个答案:

答案 0 :(得分:2)

问题

问题是使用Cache-Control响应指令必须重新验证。

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

Cache-Control: max-age=86400, private

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

验证

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

之后,我每秒触发一次testRestfulAPI。我总是从缓存中获得期望的结果。 Charles指出数据必须来自缓存,因为60秒钟没有联系服务器。

Verification using Charles

RFC 7234

此处是5.2.2.1下RFC 7234(已取代RFC 2616)的引号。它指出:

  

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

     

仅当且仅当服务器必须使用must-revalidate指令   如果未能验证表示中的请求可能导致   错误的操作,例如默默执行的财务   交易。

在阅读了这些内容之后,如果您将自己置于缓存开发人员的视野中,则可以很好地想象,当看到必须重新验证的内容时,总是会与原始服务器联系,而其他任何指令(例如max-age)都将被忽略。在我看来,缓存在实践中经常准确地表明这种行为。

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

  

“必须重新验证”响应指令指示,一旦过时,缓存就不得使用该响应来满足后续请求,而无需在原始服务器上成功进行验证。

这通常被解释为,通过将max-age与must-revalidate一起指定,您可以确定内容何时过期(在max-age秒之后),然后必须在原始服务器上进行验证,然后才能提供内容。 / p>

但是,实际上,由于上​​述原因,必须重新验证总是导致对原始服务器上每个请求的验证。

答案 1 :(得分:1)

尝试更改这些行

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

对此:

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.urlCache = sharedURLCache; // make sure sharedURLCache is accessible from here
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];