【问题标题】:AFNetworking : How to know if response is using cache or not ? 304 or 200AFNetworking:如何知道响应是否使用缓存? 304 或 200
【发布时间】:2012-08-29 14:50:22
【问题描述】:

我的问题找不到任何答案,可能是我错过了什么......

当我请求一个 url 时,我需要知道响应是来自缓存还是来自网络。

状态码是 304 还是 200 ? (但AFNetworking 总是回复 200)

对于ASIHTTPRequest,我曾经从ASIHTTPRequest 中检查“didUseCachedResponse”,这非常完美。

【问题讨论】:

  • 我知道这不是您要的,但如果您特别想要缓存或不想要缓存,您应该查看请求的 NSURLRequestCachePolicy 属性。
  • 谢谢,CachePolicy 似乎在我的代码中设置得很好,因为当我第二次询问相同的 url 时响应来自缓存。问题是我不知道如何在代码中检查它。如果响应来自缓存,我不想做一些额外的繁重代码。
  • 似乎苹果不想让你知道它是否来自缓存。我找到了一种方法,将修改日期与请求关联起来,并在 AFNetWorking 回复我时比较这个日期。不像我想要的那么干净,但可以工作......
  • AFNetworking 3.x这个问题有更新的答案吗?

标签: iphone ios asihttprequest afnetworking http-status-code-304


【解决方案1】:

我想我找到了一个解决方案来确定响应是从缓存返回还是不使用 AFNetworking 2.0。我发现每次从服务器返回新响应(状态 200,而不是 304)时,都会调用 cacheResponseBlock,它是 AFHTTPRequestOperation 的属性。如果响应应该被缓存,该块应该返回NSCachedURLResponse,如果不应该被缓存,则返回nil。这样您就可以过滤响应并仅缓存其中的一些。在这种情况下,我正在缓存来自服务器的所有响应。诀窍是,当服务器发送 304 并从缓存加载响应时,不会调用此块。所以,这是我正在使用的代码:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

BOOL __block responseFromCache = YES; // yes by default

void (^requestSuccessBlock)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject) {
    if (responseFromCache) {
        // response was returned from cache
        NSLog(@"RESPONSE FROM CACHE: %@", responseObject);
    }
    else {
        // response was returned from the server, not from cache
        NSLog(@"RESPONSE: %@", responseObject);
    }
};

void (^requestFailureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"ERROR: %@", error);
};

AFHTTPRequestOperation *operation = [manager GET:@"http://example.com/"
                                      parameters:nil
                                         success:requestSuccessBlock
                                         failure:requestFailureBlock];

[operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
    // this will be called whenever server returns status code 200, not 304
    responseFromCache = NO;
    return cachedResponse;
}];

这个解决方案对我有用,到目前为止我还没有发现任何问题。但是,如果您有更好的想法或对我的解决方案有异议,请随时发表评论!

【讨论】:

  • 我在没有测试的情况下提升了这个响应,但它看起来像是一个干净的解决方法。谢谢
  • 目前在执行繁重网络的应用中使用此功能。它处于开发阶段,但经过越来越多的测试,我仍然找不到任何问题。它只是工作。
  • 您必须小心,因为当服务器没有或不正确的缓存响应标头时不会调用此块。如果是这种情况,您会一直认为 AFNetworking 是从缓存加载的,但实际上它是从服务器加载的。还有一些其他情况不会调用此方法 - 请参阅苹果文档:developer.apple.com/library/ios/documentation/Foundation/…:
  • 这对我来说不适用于 LARGE 请求(在 OS X 上),直到我更改了 NSURLCache 磁盘和内存大小限制。我不知道确切的要求,但 1.6MB 的响应永远不会使用默认值调用响应块。
  • AFURLSessionManagerdataTaskWillCacheResponse
【解决方案2】:

似乎苹果不想让你知道它是否来自缓存。

我找到了一种方法,将修改日期与请求关联起来,并在 AFNetWorking 回复我时比较此日期。

没有我想要的那么干净,但是可以...

【讨论】:

  • 查看我的解决方案,它适用于我,您不需要存储任何额外的数据或子类化任何东西,甚至不需要添加类别。
【解决方案3】:

在AFNetworking中有一种方法可以指定应该被视为成功的状态码,它是通过响应序列化来完成的,代码如下

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

AFHTTPResponseSerializer *respSerializer = [AFHTTPResponseSerializer serializer];
NSMutableIndexSet *responseCodes = [NSMutableIndexSet indexSet];
[responseCodes addIndex:200];
[responseCodes addIndex:304];

[operation setResponseSerializer:respSerializer];

使用此代码,AFNetworking 会将 304 视为成功

【讨论】:

    【解决方案4】:

    创建您的 URLCache 类并覆盖 storeCachedResponse 方法

    class MyURLCache: URLCache {
        override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) {
    
            //adding caching header if needed
            var headers = response.allHeaderFields
            headers.removeValue(forKey: "Cache-Control")
            headers["Cache-Control"] = "max-age=\(5 * 60)" //5 min
    
            //the trick
            if (headers["isCachedReponse"] == nil){
                headers["isCachedReponse"] = "true"
            }
    
            if let
                headers = headers as? [String: String],
                let newHTTPURLResponse = HTTPURLResponse(url: response.url!, statusCode: response.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
                let newCachedResponse = CachedURLResponse(response: newHTTPURLResponse, data: cachedResponse.data)
                super.storeCachedResponse(newCachedResponse, for: request)
            }
        }
    }
    

    在 AppDelegate 中使用您的 URLCache 设置 URLCache.shared

    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
            let cache = MyURLCache(memoryCapacity: 1024 * 1024 * 500, diskCapacity: 1024 * 1024 * 500, diskPath: nil)
            URLCache.shared = cache
            return true
        }
    }
    

    在响应回调中检查响应内容的标题是否为“newResponse”键

    if (response.allHeaderFields["isCachedReponse"] == nil){
          print("not cache")
    } else {
          print("cache")
    }
    

    适用于所有版本的 AFNetworking

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-19
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 2018-10-18
      • 2013-12-08
      • 2010-09-05
      相关资源
      最近更新 更多