如何使用 NSURLSession 来确定资源是否已更改?
How to use NSURLSession to determine if resource has changed?
我正在使用 NSURLSession 从 HTTP 服务器请求 JSON 资源。服务器使用 Cache-Control 来限制资源在客户端缓存的时间。
这很好用,但我还想在内存中缓存反序列化的 JSON object,因为它经常被访问,同时继续利用 NSURLSession 中内置的 HTTP 缓存机制。
我想我可以保存一些 HTTP 响应 headers:Content-MD5
、Etag
和 Last-Modified
以及反序列化的 JSON object(我正在使用这 3 个字段,因为我注意到并非所有 HTTP 服务器 return Content-MD5
,否则它本身就足够了)。下次我收到 JSON object 的响应时,如果这 3 个字段相同,那么我可以重用之前反序列化的 JSON object.
这是确定反序列化的可靠方法吗JSON仍然有效。如果不是,我如何确定反序列化的 object 是否是最新的?
这可以通过简单的 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-modifed-since
中设置它
我创建了一个 HTTPEntityFingerprint 结构来存储一些实体 headers: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
}
}
当我从 NSURLSession
获得 NSHTTPURLResponse
时,我从中创建了一个 HTTPEntityFingerprint
并使用 HTTPEntityFingerprint.match
将其与先前存储的指纹进行比较。如果指纹匹配,那么 HTTP 资源没有改变,因此我不需要再次反序列化 JSON 响应;但是,如果指纹不匹配,那么我会反序列化 JSON 响应并保存新指纹。
此机制仅在您的服务器 returns 至少 3 个实体 headers 之一:Content-MD5
、Etag
或 Last-Modified
时有效。
有关 NSURLSession 和 NSURLCache 行为的更多详细信息
NSURLSession
通过 NSURLCache
提供的缓存是透明的,这意味着当您请求以前缓存的资源时 NSURLSession
将调用完成 handlers/delegates 就像 200 响应一样发生了。
如果缓存的响应已过期,NSURLSession 将向源服务器发送一个新请求,但会使用 Last-Modified
包含 If-Modified-Since
和 If-None-Match
headers和 Etag
实体 headers 在缓存(尽管已过期)结果中;此行为是内置的,除了启用缓存外,您无需执行任何操作。如果原始服务器 returns 304(未修改),则 NSURLSession
会将其转换为应用程序的 200 响应(使其看起来像是您获取了资源的新副本,即使它仍然是从缓存中提供)。
我正在使用 NSURLSession 从 HTTP 服务器请求 JSON 资源。服务器使用 Cache-Control 来限制资源在客户端缓存的时间。
这很好用,但我还想在内存中缓存反序列化的 JSON object,因为它经常被访问,同时继续利用 NSURLSession 中内置的 HTTP 缓存机制。
我想我可以保存一些 HTTP 响应 headers:Content-MD5
、Etag
和 Last-Modified
以及反序列化的 JSON object(我正在使用这 3 个字段,因为我注意到并非所有 HTTP 服务器 return Content-MD5
,否则它本身就足够了)。下次我收到 JSON object 的响应时,如果这 3 个字段相同,那么我可以重用之前反序列化的 JSON object.
这是确定反序列化的可靠方法吗JSON仍然有效。如果不是,我如何确定反序列化的 object 是否是最新的?
这可以通过简单的 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-modifed-since
我创建了一个 HTTPEntityFingerprint 结构来存储一些实体 headers: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
}
}
当我从 NSURLSession
获得 NSHTTPURLResponse
时,我从中创建了一个 HTTPEntityFingerprint
并使用 HTTPEntityFingerprint.match
将其与先前存储的指纹进行比较。如果指纹匹配,那么 HTTP 资源没有改变,因此我不需要再次反序列化 JSON 响应;但是,如果指纹不匹配,那么我会反序列化 JSON 响应并保存新指纹。
此机制仅在您的服务器 returns 至少 3 个实体 headers 之一:Content-MD5
、Etag
或 Last-Modified
时有效。
有关 NSURLSession 和 NSURLCache 行为的更多详细信息
NSURLSession
通过 NSURLCache
提供的缓存是透明的,这意味着当您请求以前缓存的资源时 NSURLSession
将调用完成 handlers/delegates 就像 200 响应一样发生了。
如果缓存的响应已过期,NSURLSession 将向源服务器发送一个新请求,但会使用 Last-Modified
包含 If-Modified-Since
和 If-None-Match
headers和 Etag
实体 headers 在缓存(尽管已过期)结果中;此行为是内置的,除了启用缓存外,您无需执行任何操作。如果原始服务器 returns 304(未修改),则 NSURLSession
会将其转换为应用程序的 200 响应(使其看起来像是您获取了资源的新副本,即使它仍然是从缓存中提供)。