绕过http响应头Cache-Control:如何设置缓存过期?

Cen*_*ion 9 objective-c ios afnetworking

来自服务器的所有http响应都带有标题,通知我们的应用不要缓存响应:

Cache-Control: no-cache
Pragma: no-cache
Expires: 0
Run Code Online (Sandbox Code Playgroud)

因此,如果您使用默认缓存策略"NSURLRequestUseProtocolCachePolicy"创建NSUrlRequests,则应用程序将始终从服务器加载数据.但是,我们需要缓存响应,显而易见的解决方案是将这些标头设置为某个时间(例如在后端),设置为10秒.但我对如何绕过此策略并将每个请求缓存10秒的解决方案感兴趣.

为此,您需要设置共享缓存.这可能在AppDelegate didFinishLaunchingWithOptions中完成:

NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                             diskCapacity:20 * 1024 * 1024
                                               diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
Run Code Online (Sandbox Code Playgroud)

然后,我们需要嵌入我们的代码来强制缓存响应.如果您使用AFHttpClient的实例,则可以通过覆盖以下方法并手动将缓存存储到共享缓存中来完成:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
              willCacheResponse:(NSCachedURLResponse *)cachedResponse {

  NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy];
  NSMutableData *mutableData = [[cachedResponse data] mutableCopy];
  NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;

  // ...

  return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
                                                data:mutableData
                                            userInfo:mutableUserInfo
                                       storagePolicy:storagePolicy];
}
Run Code Online (Sandbox Code Playgroud)

最后一件事是为请求设置cachePolicy.在我们的例子中,我们希望为所有请求设置相同的缓存策略.所以再次,如果您使用AFHttpClient的实例,那么可以通过覆盖以下方法来完成:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {

  NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
  request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

  return request;
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好."NSURLRequestReturnCacheDataElseLoad"使得第一次执行请求并在其他时间从缓存加载响应.问题是,目前还不清楚如何设置缓存过期时间,例如10秒.

Ste*_*ert 10

您可以实现仅返回尚未过期的缓存响应的自定义NSURLCache.

例:

#import "CustomURLCache.h"

NSString * const EXPIRES_KEY = @"cache date";
int const CACHE_EXPIRES = -10;

@implementation CustomURLCache

// static method for activating this custom cache
+(void)activate {
    CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:(2*1024*1024) diskCapacity:(2*1024*1024) diskPath:nil] ;
    [NSURLCache setSharedURLCache:urlCache];
}

-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    NSCachedURLResponse * cachedResponse = [super cachedResponseForRequest:request];
    if (cachedResponse) {
        NSDate* cacheDate = [[cachedResponse userInfo] objectForKey:EXPIRES_KEY];
        if ([cacheDate timeIntervalSinceNow] < CACHE_EXPIRES) {
            [self removeCachedResponseForRequest:request];
            cachedResponse = nil;
        }
    }

    return cachedResponse;
}

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
    NSMutableDictionary *userInfo = cachedResponse.userInfo ? [cachedResponse.userInfo mutableCopy] : [NSMutableDictionary dictionary];
    [userInfo setObject:[NSDate date] forKey:EXPIRES_KEY];
    NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy];

    [super storeCachedResponse:newCachedResponse forRequest:request];
}

@end
Run Code Online (Sandbox Code Playgroud)

如果这不能给你足够的控制,那么我将使用如下的startLoading方法实现自定义NSURLProtocol,并将其与自定义缓存结合使用.

- (void)startLoading
{
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"CacheSet" inRequest:newRequest];

    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
    if (cachedResponse) {  
        [self connection:nil didReceiveResponse:[cachedResponse response]];
        [self connection:nil didReceiveData:[cachedResponse data]];
        [self connectionDidFinishLoading:nil];
    } else {
        _connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
}
Run Code Online (Sandbox Code Playgroud)

一些链接:


Voj*_*bka 8

如果有人会感兴趣,这里是Stephanus在Swift中重写的答案:

class CustomURLCache: NSURLCache {

    // UserInfo expires key
    let kUrlCacheExpiresKey = "CacheData";

    // How long is cache data valid in seconds
    let kCacheExpireInterval:NSTimeInterval = 60*60*24*5;

    // get cache response for a request
    override func cachedResponseForRequest(request:NSURLRequest) -> NSCachedURLResponse? {
        // create empty response
        var response:NSCachedURLResponse? = nil

        // try to get cache response
        if let cachedResponse = super.cachedResponseForRequest(request) {

            // try to get userInfo
            if let userInfo = cachedResponse.userInfo {

                // get cache date
                if let cacheDate = userInfo[kUrlCacheExpiresKey] as NSDate? {

                    // check if the cache data are expired
                    if (cacheDate.timeIntervalSinceNow < -kCacheExpireInterval) {
                        // remove old cache request
                        self.removeCachedResponseForRequest(request);
                    } else {
                        // the cache request is still valid
                        response = cachedResponse
                    }
                }
            }
        }

        return response;
    }

    // store cached response
    override func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest: NSURLRequest) {
        // create userInfo dictionary
        var userInfo = NSMutableDictionary()
        if let cachedUserInfo = cachedResponse.userInfo {
            userInfo = NSMutableDictionary(dictionary:cachedUserInfo)
        }
        // add current date to the UserInfo
        userInfo[kUrlCacheExpiresKey] = NSDate()

        // create new cached response
        let newCachedResponse = NSCachedURLResponse(response:cachedResponse.response, data:cachedResponse.data, userInfo:userInfo,storagePolicy:cachedResponse.storagePolicy)
        super.storeCachedResponse(newCachedResponse, forRequest:forRequest)

    }

}
Run Code Online (Sandbox Code Playgroud)