Bypassing http response header Cache-Control: how to set cache expiration?

后端 未结 3 702
面向向阳花
面向向阳花 2020-12-25 08:56

All http responses from a server come with the headers that inform our app not to cache the responses:

Cache-Control: no-cache
Pragma: no-cache
Expires: 0


        
相关标签:
3条回答
  • 2020-12-25 09:09

    You can implement a custom NSURLCache that only returns cached responses that has not expired.

    Example:

    #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
    

    If this does not give you enough control then I would implement a custom NSURLProtocol with a startLoading method as below and use it in conjunction with the custom cache.

    - (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];
        }
    }
    

    Some links:

    • Useful info on NSURLCache
    • Creating a custom NSURLProtocol
    0 讨论(0)
  • 2020-12-25 09:21

    If somebody would be interested, here is Stephanus answer rewritten in 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)
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-25 09:27

    Another possible solution is to modify the response object and clobber the Cache-Control headers from the server and replace them with your own desired values.

    There are two places you could do that.

    You could do it in a NSURLSessionDataDelegate in func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void), but if you do it there, then can no longer use the usual completion handler-based methods getting results from session tasks.

    Another place you could do it is by defining a custom NSURLProtocol, which intercepts HTTP and HTTPS responses and modifies them.

    0 讨论(0)
提交回复
热议问题