如何使用NSURLSession来确定资源是否已经改变?
我正在使用NSURLSession从HTTP服务器请求JSON资源。 服务器使用Cache-Control来限制资源在客户端上的caching时间。
这很好,但我也想caching反序列化的JSON对象,因为它经常被访问,同时继续利用NSURLSession中内置的HTTPcaching机制。
我想我可以保存一些HTTP响应头: Content-MD5
, Etag
和Last-Modified
与反序列化的JSON对象(我使用这3个字段,因为我注意到并不是所有的HTTP服务器都返回Content-MD5
,否则这本身就足够了)。 下一次我收到JSON对象的响应,如果这3个字段是相同的,那么我可以重用先前反序列化的JSON对象。
这是一个强大的方式来确定被取消的JSON仍然有效。 如果不是,我该如何确定反序列化的对象是否是最新的?
我创build了一个HTTPEntityFingerprint结构,它存储了一些实体标题: Content-MD5
, Etag
和Last-Modified
。
import Foundation struct HTTPEntityFingerprint { let contentMD5 : String? let etag : String? let lastModified : String? } extension HTTPEntityFingerprint { init?(response : NSURLResponse) { if let httpResponse = response as? NSHTTPURLResponse { let h = httpResponse.allHeaderFields contentMD5 = h["Content-MD5"] as? String etag = h["Etag"] as? String lastModified = h["Last-Modified"] as? String if contentMD5 == nil && etag == nil && lastModified == nil { return nil } } else { return nil } } static func match(first : HTTPEntityFingerprint?, second : HTTPEntityFingerprint?) -> Bool { if let a = first, b = second { if let md5A = a.contentMD5, md5B = b.contentMD5 { return md5A == md5B } if let etagA = a.etag, etagB = b.etag { return etagA == etagB } if let lastA = a.lastModified, lastB = b.lastModified { return lastA == lastB } } return false } }
当我从NSHTTPURLResponse
获得一个NSURLSession
,我从它创build一个HTTPEntityFingerprint
,并使用HTTPEntityFingerprint.match
将它与以前存储的指纹进行HTTPEntityFingerprint.match
。 如果指纹匹配,那么HTTP资源没有改变,因此我不需要反序列化JSON响应。 但是,如果指纹不匹配,那么我将反序列化JSON响应并保存新的指纹。
只有当您的服务器至less返回3个实体标题之一时,此机制才有效: Content-MD5
, Etag
或Last-Modified
。
有关NSURLSession和NSURLCache行为的更多详细信息
NSURLSession
通过NSURLCache
提供的caching是透明的,这意味着当你请求一个先前caching的资源时, NSURLSession
将会像调用200响应一样调用完成处理程序/委托。
如果caching的响应已经过期,则NSURLSession将向原始服务器发送一个新的请求,但是将包含使用caching中的Last-Modified
和Etag
实体头部的If-Modified-Since
和If-None-Match
头部(尽pipe过期)结果; 此行为是内置的,除了启用caching之外,您不必执行任何操作。 如果源服务器返回一个304(未修改),那么NSURLSession
将把它转换为应用程序的200响应(使得它看起来像你获取资源的新副本,即使它仍然从caching中提供)。
这可以通过简单的HTTP标准响应来完成。
假设之前的回应如下所示:
{ status code: 200, headers { "Accept-Ranges" = bytes; Connection = "Keep-Alive"; "Content-Length" = 47616; Date = "Thu, 23 Jul 2015 10:47:56 GMT"; "Keep-Alive" = "timeout=5, max=100"; "Last-Modified" = "Tue, 07 Jul 2015 11:28:46 GMT"; Server = Apache; } }
现在使用下面来告诉服务器不发送date,如果它没有被修改,因为。
NSURLSession
是一个可configuration的容器,你可能需要使用http选项“IF-Modified-Since”
下载资源前使用下面的configuration种类,
NSURLSessionConfiguration *backgroundConfigurationObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"myBackgroundSessionIdentifier"]; [backgroundConfigurationObject setHTTPAdditionalHeaders: @{@"If-Modified-Since": @"Tue, 07 Jul 2015 11:28:46 GMT"}];
如果资源例如不会从上面设置的date改变,那么下面的委托将被调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) downloadTask.response; if([httpResponse statusCode] == 304) //resource is not modified since last download date }
检查downloadTask.response
状态代码是304 ..然后资源不被修改,资源不下载。
注意保存一些NSUserDefaults
的上一次成功的完整下载date,将其设置为if-modifed-since