在对 canInitWithRequest 的 YES 响应后不要求加载 NSURLProtocol

NSURLProtocol isn#39;t asked to load after YES response to canInitWithRequest(在对 canInitWithRequest 的 YES 响应后不要求加载 NSURLProtocol)

本文介绍了在对 canInitWithRequest 的 YES 响应后不要求加载 NSURLProtocol的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在注册 NSURLProtocol 的实现,以对某些 URL 请求进行一些自定义处理(我通过在它们上设置属性来标记这些请求).这些 URL 请求来自 UIWebView 的加载方法.

I'm registering an implementation of NSURLProtocol to do some custom handling of certain URL requests (I tag these requests by setting properties on them). These URL requests are coming from a UIWebView's load method.

  1. 创建 UI Web 视图(启动后首次加载)
  2. 加载内容
  3. 销毁网页视图
  4. 创建一个新的网络视图(后续加载)
  5. 加载内容

我发现第 2 步和第 5 步之间的行为明显不同.我的 NSURLProtocol 实现管理缓存的数据,旨在处理请求.如果我没有在请求中检测到我的属性,我会再次检查特定 URL(用于调试).在任何一种情况下 canInitWithRequest 都会返回 YES:

I'm seeing significantly different behavior between steps 2 and 5. My NSURLProtocol implementation manages cached data and is designed to handle the requests. If I don't detect my property in a request, I do a second check for a specific URL (for debugging). In either case canInitWithRequest will return YES:

启动后首次加载:

2014-10-15 11:37:11.403 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ebbb40> https://example.com/ 
2014-10-15 11:37:11.404 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15dc8da0> https://example.com/ 
2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee5ef0> https://example.com/ 
2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee6240> https://example.com/ 
2014-10-15 11:37:11.410 MYApp[5813:60b] MYURLProtocol initWithRequest:cachedResponse:client: Request: https://example.com/
2014-10-15 11:37:11.411 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee69d0> https://example.com/ 
2014-10-15 11:37:11.415 MYApp[5813:9c07] MYURLProtocol startLoading Loading <0x15ee6240> https://example.com/ 
... A bunch of loading of assets occurs (cached responses) ...
2014-10-15 11:37:12.497 MYApp[5813:60b] MyWebViewController webViewDidFinishLoad: Finished loading

其他人指出,对于同一个资产,协议有多次调用,这不是问题,但有趣的是,每次调用它时都会使用一个新对象,而且是第 4 次(4) 传递给 startLoading 的对象.不过,这里没有真正的担忧.

Others have pointed out that there are multiple calls to the protocol about the same asset, and this is not a concern, though it's interesting to note that each time it is called with a new object, and it is the 4th (of 4) object that gets passed to startLoading. Still, no real concerns here.

后续加载:

2014-10-15 11:11:27.466 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x1727c310> https://example.com/ 
2014-10-15 11:11:27.467 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x145b1d90> https://example.com/ 
2014-10-15 11:11:27.488 MYApp[5782:560f] MYURLProtocol canInitWithRequest: Matched URL in request: <0x17266060> https://example.com/
2014-10-15 11:11:27.669 MYApp[5782:60b] MYWebViewController webViewDidFinishLoad: Finished loading

在我看来,这是出乎意料的行为.似乎在第三次将属性传递给 canInitWithRequest 时,该属性已从请求中剥离,然后,即使我们响应 YES,我们也从未真正被初始化——页面只是完整地返回到 UIWebView,没有后续的资产请求.以下是创建请求时的样子:

This is where the behavior, in my view, is unexpected. It appears that property has been stripped off of the request by the third time it is passed to canInitWithRequest, and then, even though we respond YES we never actually get inited -- the page is simply returned to the UIWebView in its entirety, with no subsequent requests for assets. Here is what the request looks like when it is created:

NSURLRequest *request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[self.webView loadRequest:request];

为什么,当我说我的协议可以处理请求时,它没有机会这样做吗?我的猜测是答案在于 UIWebView 本身的实现.如果我真的希望我的协议成为负责加载的实体,关于如何解决这个问题的任何想法?

Why, when I say that my protocol can handle the request, is it not being given the opportunity to do so? My guess is that the answer is in the implementation of UIWebView itself. Any thoughts on how to work around this if I really want my protocol to be the responsible entity for loading?

推荐答案

我能够通过缓存清除 UIWebView 缓存而不破坏 NSURLCache 来解决此问题.

I was able to workaround this issue by cache-busting the UIWebView cache, while not busting the NSURLCache.

  1. 为原始请求的查询参数添加一个唯一参数.我选择了key=000000",其中的值是以零开头的六位随机数.
  2. 在协议中,剥离 canonicalRequestForRequest:initWithRequest:cachedResponse:client
  3. 中的键
  1. Add a unique param to the query params of the original request. I chose 'key=000000' where the value is zero-led six digit random number.
  2. In the protocol, strip the key in canonicalRequestForRequest: and in initWithRequest:cachedResponse:client

我的剥离代码看起来像这样(可能有一种更简洁的方式来剥离参数,但这是可行的):

My stripping code looks like this (there might be a cleaner way to strip the param, but this works):

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    NSURLRequest *canonicalRequest = request;
    BOOL myProtocolRequest = [[NSURLProtocol propertyForKey:kMYProtocolRequest inRequest:request] boolValue];
    if (myProtocolRequest)
    {
        NSMutableURLRequest *mutableRequest = [request mutableCopyWorkaround];
        NSString *originalURLString = mutableRequest.URL.absoluteString;
        NSString *regexString = [NSString stringWithFormat:@"(?:[?&])(key=[[:digit:]]{%d}&*)", kMYKeyLength];

        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:0];
        NSTextCheckingResult *result = [regex firstMatchInString:originalURLString options:0 range:NSMakeRange(0, originalURLString.length)];
        if (result.numberOfRanges > 1)
        {
            NSRange keyRange = [result rangeAtIndex:1];
            NSLog(@"Removing '%@' from request", [originalURLString substringWithRange:keyRange]);
            NSString *replacementURLString = [originalURLString stringByReplacingCharactersInRange:keyRange withString:@""];
            mutableRequest.URL = [NSURL URLWithString:replacementURLString];
            canonicalRequest = mutableRequest;
        }
    }

    return canonicalRequest;
}

我的初始化代码如下所示:

My init code looks like this:

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{
    self = [super initWithRequest:[MYURLProtocol canonicalRequestForRequest:request] cachedResponse:cachedResponse client:client];
    return self;
}

我不喜欢我必须这样做,但我终于得到了我想要的行为.希望它可以帮助那里的人.

I don't like that I have to do this, but I'm finally getting exactly the behavior I want. Hopefully it helps someone out there.

这篇关于在对 canInitWithRequest 的 YES 响应后不要求加载 NSURLProtocol的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:在对 canInitWithRequest 的 YES 响应后不要求加载 NSURLProtocol