14

我正在使用 AFNetworking,需要在一个响应中缓存数据几分钟。所以我在应用程序委托中设置 NSUrlCache ,然后在我的请求中设置它:

NSMutableURLRequest *request = //obtain request; 
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

那么如何设置过期日期:如果数据是在 n 分钟前加载的,则询问服务器而不是磁盘的响应?

编辑:

假设服务器不支持缓存,我需要在代码中管理它。

4

3 回答 3

19

所以,我找到了解决方案。

这个想法是使用connection:willCacheResponse:方法。在缓存响应之前,它将被执行,在那里我们可以更改响应并返回新的,或者返回 nil 并且不会缓存响应。当我使用 AFNetworking 时,有一个很好的操作方法:

- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;

添加代码:

  [operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
    if([connection currentRequest].cachePolicy == NSURLRequestUseProtocolCachePolicy) {
      cachedResponse = [cachedResponse responseWithExpirationDuration:60];
    }
    return cachedResponse;
  }];

来自responseWithExpirationDuration类别:

@interface NSCachedURLResponse (Expiration)
-(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration;
@end

@implementation NSCachedURLResponse (Expiration)

-(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration {
  NSCachedURLResponse* cachedResponse = self;
  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];
  NSDictionary *headers = [httpResponse allHeaderFields];
  NSMutableDictionary* newHeaders = [headers mutableCopy];

  newHeaders[@"Cache-Control"] = [NSString stringWithFormat:@"max-age=%i", duration];
  [newHeaders removeObjectForKey:@"Expires"];
  [newHeaders removeObjectForKey:@"s-maxage"];

  NSHTTPURLResponse* newResponse = [[NSHTTPURLResponse alloc] initWithURL:httpResponse.URL
                                                               statusCode:httpResponse.statusCode
                                                              HTTPVersion:@"HTTP/1.1"
                                                             headerFields:newHeaders];

  cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse
                                                            data:[cachedResponse.data mutableCopy]
                                                        userInfo:newHeaders
                                                   storagePolicy:cachedResponse.storagePolicy];
  return cachedResponse;
}

@end

因此,我们根据http/1.1在 http 标头中以秒为单位设置过期 为此我们需要设置标头之一:Expires,Cache-Control:s-maxage 或 max-age 然后创建新的缓存响应,因为属性是只读,并返回新对象。

于 2013-11-08T14:53:16.207 回答
9

Swift 等效于使用 URLSession 的 @HotJard 解决方案

extension CachedURLResponse {
    func response(withExpirationDuration duration: Int) -> CachedURLResponse {
        var cachedResponse = self
        if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url{

            headers["Cache-Control"] = "max-age=\(duration)"
            headers.removeValue(forKey: "Expires")
            headers.removeValue(forKey: "s-maxage")

            if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
            cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: cachedResponse.storagePolicy)
            }
        }
        return cachedResponse
    }
}

然后在您的自定义类中实现 URLSessionDataDelegate 协议

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {

    if dataTask.currentRequest?.cachePolicy == .useProtocolCachePolicy {
        let newResponse = proposedResponse.response(withExpirationDuration: 60)
        completionHandler(newResponse)
    }else {
        completionHandler(proposedResponse)
    }
}

不要忘记创建您的配置和会话,将您的自定义类作为委托参考传递,例如

let session = URLSession(
        configuration: URLSession.shared.configuration,
        delegate: *delegateReference*,
        delegateQueue: URLSession.shared.delegateQueue
    )
let task = session.dataTask(with: request)
task.resume()
于 2017-09-11T14:49:20.430 回答
1

中的响应过期由HTTP 响应中NSURLCache的标头控制。Cache-Control

编辑我看到你已经更新了你的问题。如果服务器未在响应中提供 Cache-Control 标头,则不会对其进行缓存。对该端点的每个请求都将加载该端点,而不是返回一个缓存的响应。

于 2013-11-08T09:59:37.270 回答