草庐IT

ios - 绕过 http 响应头 Cache-Control : how to set cache expiration?

coder 2023-07-29 原文

来自服务器的所有 HTTP 响应都带有 header ,通知我们的应用不要缓存响应:

Cache-Control: no-cache
Pragma: no-cache
Expires: 0

因此,如果您使用默认缓存策略“NSURLRequestUseProtocolCachePolicy”发出 NSUrlRequests,那么应用程序将始终从服务器加载数据。但是,我们需要缓存响应,显而易见的解决方案是将这些 header 设置为某个时间,例如(在后端),设置为 10 秒。但我对如何绕过此策略并将每个请求缓存 10 秒的解决方案感兴趣。

为此你需要设置共享缓存。这可能在 AppDelegate didFinishLaunchingWithOptions 中完成:

NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                             diskCapacity:20 * 1024 * 1024
                                               diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];

然后,我们需要嵌入我们的代码来强制缓存响应。如果您使用 AFHttpClient 的实例,则可以通过覆盖以下方法并将缓存手动存储到共享缓存中来完成:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
              willCacheResponse:(NSCachedURLResponse *)cachedResponse {

  NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy];
  NSMutableData *mutableData = [[cachedResponse data] mutableCopy];
  NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;

  // ...

  return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
                                                data:mutableData
                                            userInfo:mutableUserInfo
                                       storagePolicy:storagePolicy];
}

最后一件事是为请求设置 cachePolicy。在我们的例子中,我们希望为所有请求设置相同的缓存策略。再次强调,如果您使用 AFHttpClient 的实例,则可以通过覆盖以下方法来完成:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {

  NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
  request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

  return request;
}

到目前为止一切顺利。 “NSURLRequestReturnCacheDataElseLoad”使得第一次执行请求并在所有其他时间从缓存加载响应。问题是,不清楚如何设置缓存过期时间,比如10秒。

最佳答案

您可以实现一个自定义的 NSURLCache,它只返回尚未过期的缓存响应。

例子:

#import "CustomURLCache.h"

NSString * const EXPIRES_KEY = @"cache date";
int const CACHE_EXPIRES = -10;

@implementation CustomURLCache

// static method for activating this custom cache
+(void)activate {
    CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:(2*1024*1024) diskCapacity:(2*1024*1024) diskPath:nil] ;
    [NSURLCache setSharedURLCache:urlCache];
}

-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    NSCachedURLResponse * cachedResponse = [super cachedResponseForRequest:request];
    if (cachedResponse) {
        NSDate* cacheDate = [[cachedResponse userInfo] objectForKey:EXPIRES_KEY];
        if ([cacheDate timeIntervalSinceNow] < CACHE_EXPIRES) {
            [self removeCachedResponseForRequest:request];
            cachedResponse = nil;
        }
    }

    return cachedResponse;
}

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
    NSMutableDictionary *userInfo = cachedResponse.userInfo ? [cachedResponse.userInfo mutableCopy] : [NSMutableDictionary dictionary];
    [userInfo setObject:[NSDate date] forKey:EXPIRES_KEY];
    NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy];

    [super storeCachedResponse:newCachedResponse forRequest:request];
}

@end

如果这不能给你足够的控制权,那么我将使用如下所示的 startLoading 方法实现自定义 NSURLProtocol,并将其与自定义缓存结合使用。

- (void)startLoading
{
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"CacheSet" inRequest:newRequest];

    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
    if (cachedResponse) {  
        [self connection:nil didReceiveResponse:[cachedResponse response]];
        [self connection:nil didReceiveData:[cachedResponse data]];
        [self connectionDidFinishLoading:nil];
    } else {
        _connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
}

一些链接:

关于ios - 绕过 http 响应头 Cache-Control : how to set cache expiration?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19840688/

有关ios - 绕过 http 响应头 Cache-Control : how to set cache expiration?的更多相关文章

  1. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  6. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  7. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  8. ruby-on-rails - Rails - 从命名路由中提取 HTTP 动词 - 2

    Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba

  9. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  10. ruby-on-rails - Heroku 吃掉了我的自定义 HTTP header - 2

    我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份​​验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby​​脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara

随机推荐