我正在尝试找出从我的API服务器处理304 Not Modified响应的最佳方法。在每个请求中,我通过覆盖If-None-Match
并设置标头值来传递-requestWithMethod:URLString:parameters:error:
中的etag。
在返回304的情况下,我没有得到回应。在这种情况下,我从Core Data存储中获取缓存数据,并在GET:
方法完成处理程序中返回。
是否有更好的方法在响应序列化器中返回缓存值,还是有另一种最佳实践我缺少?我应该缓存原始200响应中的响应数据吗?
欢迎指导。
答案 0 :(得分:2)
我认为这在很大程度上取决于您的情况,但您并非 将数据存储在Core Data中。
如果服务器响应正确(看起来像是这样),这应该与NSURLCache
透明地工作。
初始请求没有电子标签,服务器返回响应标题&身体。 NSURLCache
会注意到响应是可缓存的,并将其存储在内部sqlite缓存数据库中。
在下一个请求中,它将发送电子标签,服务器响应304 Not Modified,缓存将为您建立缓存数据库的响应。你的回调将收到200回复(你永远不会看到304)。
在实践中,这是透明的,但我总是要小心保管它,以确保它正确缓存,或者通过Charles Proxy观察网络流量,或者通过手动检查数据库(您可以在应用程序的缓存目录)。
答案 1 :(得分:1)
我处理的应用程序严重依赖于NSURLCache
。我的一位同事也注意到了这类问题,并通过添加AFURLConnectionOperation+CacheControlBugFix
类别来解决这个问题,该类别也会缓存304。我搜索了一下,我不确定这是不是原创的,或者他是否在其他地方找到了一些,但我找不到原始来源。这是类别,您应该将其导入到您希望使用的任何相关位置,或者AppName-Prefix.pch
,如果您想要它在任何地方:
AFURLConnectionOperation+CacheControlBugFix.h
的内容:
#import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
@interface AFURLConnectionOperation (CacheControlBugFix)
@end
AFURLConnectionOperation+CacheControlBugFix.m
的内容:
#import "AFURLConnectionOperation+CacheControlBugFix.h"
#import <AFDownloadRequestOperation/AFDownloadRequestOperation.h>
@implementation AFURLConnectionOperation (CacheControlBugFix)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzle:@selector(finish) replacement:@selector(da_finish)];
[self swizzle:@selector(connection:willCacheResponse:) replacement:@selector(da_connection:willCacheResponse:)];
});
}
- (void)da_finish {
if ([self isKindOfClass:[AFDownloadRequestOperation class]]) {
[self da_finish];
return;
}
[self setValue:@(3) forKey:@"state"];
// Part 1 of fix for 304 URL Caching bug.
// We force all 200 request and 304 requests to be cached here, so that NSURLConnection's default behavior of not caching is corrected.
NSHTTPURLResponse *response = (NSHTTPURLResponse *) [self response];
if ([response isKindOfClass:[NSHTTPURLResponse class]] && [response statusCode] == 200) {
NSCachedURLResponse *cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:self.responseData];
NSMutableURLRequest *urlRequest = [self.request mutableCopy];
urlRequest.cachePolicy = NSURLRequestUseProtocolCachePolicy; // Allow caching of all requests, even ones that have their cache policy set to not allow responses from cache
// Make sure that we aren't here because the request was canceled. If it was, we do not want to cache.
if ([self.error code] != NSURLErrorCancelled) {
[[NSURLCache sharedURLCache] storeCachedResponse:cachedURLResponse forRequest:urlRequest];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
- (NSCachedURLResponse *)da_connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
if ([self isKindOfClass:[AFDownloadRequestOperation class]]) {
return [self da_connection:connection willCacheResponse:cachedResponse];
}
// Part 2 of fix for 304 URL Caching bug. Since we'll force all 200 requests (as well as 304 requests, which masquerade as 200 in NSURLConnection) to be cached, we don't need NSURLConnection to store it's cache in the normal way.
return nil;
}
+ (void)swizzle:(SEL)originalSelector replacement:(SEL)swizzledSelector {
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@end