Search code examples
objective-ccachingnsurlrequestnsurlcache

NSCachedURLResponse ignores HTTPBody


I'm creating a NSMutableURLRequest using this code:

NSString *urlString = @"http://192.168.1.111/api";
NSURL *url = [NSURL URLWithString:urlString];

NSString *postBody = [NSString stringWithFormat:@"param=%@", param];
NSData *postBodyData = [postBody dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postBodyLength = [@( postBodyData.length ) stringValue];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

request.URL = url;
request.HTTPMethod = @"POST";
request.HTTPBody = postBodyData;
request.cachePolicy = NSURLRequestUseProtocolCachePolicy;

[request setValue:postBodyLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

and this code to make the request:

NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

if (cachedResponse && cachedResponse.data.length > 0) {
    NSError *serializationError = nil;

    id cachedResponseData = [NSJSONSerialization JSONObjectWithData:cachedResponse.data options:0 error:&serializationError];

    if (!serializationError) {
        NSLog(@"WEB: %@:%@ cachedResponseData: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), cachedResponseData);

        [self showTranslations:cachedResponseData forWords:queryStrings withCallback:completion];
    }
} else {
    AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    requestOperation.responseSerializer = [AFJSONResponseSerializer serializer];

    [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"WEB: %@:%@ responseObject: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"WEB: %@:%@ error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), error);
    }];

    [[NSOperationQueue mainQueue] addOperation:requestOperation];
}

If I first run it with the param variable equal to "first", if goes through the else block and makes the request. On a second run, it goes through the if block and retrieves the response data from cache. But if I run it again with the param variable equal to "second", it goes through the if block again and retrieves from cache the value coresponding with the first request. How can I make it work like it should and realize that's a different request and not take it from cache?


Solution

  • AFNetworking may be doing something strange; I can only tell you how to handle it with the native APIs, so this may or may not be perfectly correct in your case.

    If memory serves, POST requests are not typically retrieved from the cache by the operating system, because they are not considered idempotent. So if you're seeing caching, it is probably happening somewhere outside your device such as a web proxy.

    With that said, specifying NSURLRequestReloadIgnoringCacheData should prevent it from using cached data. Or you can prevent it from getting stored in the cache at all by writing a custom cache handling callback and returning nil instead of an NSCachedURLResponse object.

    But the real problem here is that you're querying the cache directly instead of letting the OS handle it. The cache has no notion of request types or POST bodies. It is just looking at the URL. If you really want to cache POST requests for some reason, you'll have to concatenate the URL and the body data in some consistent way, then create a new request object for that modified URL, and use the concatenated request object when adding data to and retrieving data from the cache.