似乎无法使用 Alamofire 获取缓存的响应

Posted

技术标签:

【中文标题】似乎无法使用 Alamofire 获取缓存的响应【英文标题】:Cannot seem to fetch cached response with Alamofire 【发布时间】:2021-09-22 03:41:18 【问题描述】:

我正在尝试在 Alamofire 的 ResponseCacher 的帮助下缓存 JSON 响应 (https://api.github.com/search/repositories),在客户端中的有效期为 2 小时。在one of the posts in SO,Alamofire 的 Jon 建议修改 ResponseCacher 中的 CachedURLResponse。响应没有 Cache-Control 或任何缓存标头。所以我在响应中手动添加了缓存控制标头。根据我的理解,我还整理了会话配置代码,但是我似乎无法让缓存与覆盖的到期时间一起工作。会话定义如下:

let sessionConfiguration = URLSessionConfiguration.af.default
sessionConfiguration.requestCachePolicy = .useProtocolCachePolicy
sessionConfiguration.urlCache = URLCache(memoryCapacity: 20 * 1024 * 1024, diskCapacity: 100 * 1024 * 1024)

let responseCacher = ResponseCacher(behavior: .modify  _, response in
  return CachedURLResponse(
    response: response.response,
    data: response.data,
    userInfo: userInfo,
    storagePolicy: .allowed)
    .response(with: 7200)
)

let aSession = Session(configuration: sessionConfiguration, cachedResponseHandler: responseCacher)

其中响应​​修饰符定义如下

extension CachedURLResponse 
    func response(with expirationDuration: Int) -> CachedURLResponse 
        var cachedResponse = self
        if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url

            headers["Cache-Control"] = "private, max-age=\(expirationDuration)"
            headers.removeValue(forKey: "Expires")
            headers.removeValue(forKey: "s-maxage")

            if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) 
            cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: cachedResponse.storagePolicy)
            
        
        return cachedResponse
    

如果我将 requestCachePolicy 设置为.useProtocolCachePolicy,当网络离线时,我不会得到任何缓存响应。如果我将 requestCachePolicy 设置为.returnCacheDataElseLoad,则缓存永不过期。我需要缓存我的响应,但也要让它在 2 小时后过期,并在网络离线时返回缓存的响应。

我是缓存方面的初学者,所以有些地方我在没有完全理解它是如何工作的情况下取得了一些飞跃。部分代码取自 SO。

【问题讨论】:

【参考方案1】:

处理缓存的第一件事是 URLCache。您需要覆盖默认的 URLCache 并通过添加缓存控制标头来强制缓存。

这样的事情应该可以工作:

let sessionConfiguration = URLSessionConfiguration.af.default
sessionConfiguration.urlCache = ForceCache.makeForceCache()

let aSession = Session(configuration: sessionConfiguration)

/// The server does not return proper cache control headers for caching so we override it here
private class ForceCache: URLCache 

    static func makeForceCache() -> URLCache 
        let memoryCapacity = 20 * 1_024 * 1_024
        // According to https://developer.apple.com/documentation/foundation/nsurlsessiondatadelegate/1411612-urlsession?language=objc
        // The response size is small enough to reasonably fit within the cache.
        // (For example, if you provide a disk cache, the response must be no larger than
        // about 5% of the disk cache size.)
        let diskCapacity = 400 * 1_024 * 1_024
        let path = "org.something.something"

        return ForceCache(memoryCapacity: memoryCapacity,
                          diskCapacity: diskCapacity,
                          diskPath: path)
    

    private let constSecondsToKeepOnDisk = 2 * 60 * 60 // 2 hours

    override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) 
        var customCachedResponse = cachedResponse
        // Set custom Cache-Control
        if let response = cachedResponse.response as? HTTPURLResponse,
           var newHeaders = response.allHeaderFields as? [String: String] 
            newHeaders["Cache-Control"] = "public, max-age=\(constSecondsToKeepOnDisk)"
            newHeaders["Expires"] = nil
            newHeaders["s-maxage"] = nil

            if let url = response.url,
               // Consider only specific request
               url.absoluteString.contains("search/repositories"),
               let newResponse = HTTPURLResponse(url: url,
                                                 statusCode: response.statusCode,
                                                 httpVersion: "HTTP/1.1", headerFields: newHeaders) 
                customCachedResponse = CachedURLResponse(response: newResponse,
                                                         data: cachedResponse.data,
                                                         userInfo: cachedResponse.userInfo,
                                                         storagePolicy: cachedResponse.storagePolicy)
            
        
        super.storeCachedResponse(customCachedResponse, for: request)
    

为了完整起见,我应该补充一点,通常缓存应该由服务器处理。如果服务器发送您不应该缓存请求,这意味着该决定背后有充分的理由(或设计错误)。

【讨论】:

这对你有用吗?因为当我断开网络时没有返回缓存的 JSON。

以上是关于似乎无法使用 Alamofire 获取缓存的响应的主要内容,如果未能解决你的问题,请参考以下文章

如何检查网络响应来自 Alamofire 请求中的缓存或服务器?

尝试使用 Alamofire 缓存但没有结果

在 Alamofire 中检测缓存的响应

使用 Moya 获取响应标头

alamofire 499 使用标头和参数发出获取请求时

如何在 Alamofire 中禁用缓存