如何使用NSURLSession来确定资源是否已经改变



我使用NSURLSession从HTTP服务器请求JSON资源。服务器使用Cache-Control来限制资源在客户端缓存的时间。

这工作得很好,但是我也想在内存中缓存一个反序列化的JSON对象,因为它经常被访问,同时继续利用NSURLSession内置的HTTP缓存机制。

我想我可以保存一些HTTP响应头:Content-MD5, EtagLast-Modified以及反序列化的JSON对象(我使用这3个字段,因为我注意到并非所有HTTP服务器都返回Content-MD5,否则这本身就足够了)。下次我收到JSON对象的响应时,如果这3个字段相同,那么我可以重用先前反序列化的JSON对象。

这是确定反序列化JSON是否仍然有效的可靠方法吗?如果不是,如何确定反序列化对象是否是最新的?

我创建了一个HTTPEntityFingerprint结构,它存储了一些实体标头:Content-MD5EtagLast-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
    }
}

当我从NSURLSession中获得NSHTTPURLResponse时,我从中创建HTTPEntityFingerprint,并使用HTTPEntityFingerprint.match将其与先前存储的指纹进行比较。如果指纹匹配,则HTTP资源没有更改,因此我不需要再次反序列化JSON响应;但是,如果指纹不匹配,那么我反序列化JSON响应并保存新的指纹。

此机制仅当您的服务器返回至少一个实体头:Content-MD5, Etag,或Last-Modified

关于NSURLSession和NSURLCache行为的更多细节

NSURLSession通过NSURLCache提供的缓存是透明的,这意味着当您请求先前缓存的资源时,NSURLSession将调用完成处理程序/委托,就像发生了200响应一样。

如果缓存的响应已经过期,那么NSURLSession将向源服务器发送一个新的请求,但是将使用缓存(尽管过期)结果中的Last-ModifiedEtag实体头包含If-Modified-SinceIf-None-Match头;这种行为是内置的,除了启用缓存之外,您不需要做任何事情。如果源服务器返回304(未修改),那么NSURLSession将把它转换为应用程序的200响应(使其看起来像您获取了资源的新副本,即使它仍然从缓存中提供)。

这可以通过简单的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;
} }

现在使用下面的命令告诉服务器,如果以后没有修改过,就不要发送日期。

NSURLSession是一个可配置的容器,你可能需要使用http选项"IF-Modified-Since"

在下载资源之前使用下面的配置类型,

    NSURLSessionConfiguration *backgroundConfigurationObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"myBackgroundSessionIdentifier"];
    [backgroundConfigurationObject setHTTPAdditionalHeaders:
     @{@"If-Modified-Since": @"Tue, 07 Jul 2015 11:28:46 GMT"}];

如果资源从上面设定的日期起没有变化,那么下面的委托将被调用

    - (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中设置if- modified -since

相关内容

  • 没有找到相关文章

最新更新