我需要传递一些额外的信息以及UIWebView loadRequest:
,以便它到达我的NSURLProtocol
实现。信息不能绑定到NSURLRequest
因为信息也必须与NSURLRequest mainDocumentURL
一起保留。所以我对NSURL
进行了子类化,并用它构建了NSURLRequest
。我已经知道到达NSURLProtocol startLoading
的NSURLRequest
不是我提供给UIWebView loadRequest
的实例,所以我也实现了NSURL copyWithZone
,天真地期望URL加载系统会使用它。
现在,NSURLProtocol canInitWithRequest
不是像人们合理预期的那样被调用一次,而是在startLoading
之前至少调用 4 次。前 2 次,传入NSURLRequest
仍然包含我的自定义NSURL
实现。然后一个名为 CFURLCopyAbsoluteURL
的不幸内部代码要求absoluteURL
我的自定义NSURL
,下一个canInitWithRequest
(以及随后的startLoading
)已经得到了一个全新的NSURLRequest
,其中包含新鲜的NSURL
。 copyWithZone
永远不会被调用,我的子类化NSURL
丢失了。
在我放弃并实施一个劣质且脆弱的解决方案并将内容直接附加到 URL 字符串之前,我想问问更高级别的向导,他们是否看到了如何在NSURLProtocol
雷达上捕捉初始眨眼的方法,或者如何诱使CFURLCopyAbsoluteURL
携带我的自定义实例。我试图通过再次返回自定义 NSURL 类的新实例来破解NSURL absoluteURL
,但它没有帮助。我已经看到了NSURLProtocol setProperty
功能的一些希望,但现在它似乎毫无用处。URL加载系统愉快地创建了所有内容的新实例,并且到达NSURLProtocol
NSURLRequest
似乎与偶然输入UIWebView
的实例相同。
更新:好的,我想让帖子尽可能简短,但即使是第一个回复也是要求技术背景,所以我们开始了:我在应用程序中有多个UIWebView
。这些视图可以并发运行请求,并且绝对可以运行针对同一 URL 的请求。它就像桌面浏览器中的选项卡。但是我需要区分哪个UIWebView
是到达NSURLProtocol
的每个特定NSURLRequest
的起源。我需要每个 URL 请求都带有一个上下文。我不能简单地将 URL 映射到数据,因为多个UIWebViews
可能随时加载相同的 URL。
更新2:将上下文信息附加到NSURL
是首选,据我所知,这是唯一可用的信息。问题是对页面内引用的资源(图像等)的请求根本不经过UIWebViewDelegate
,最终直接进入NSURLProtocol
。在NSURLProtocol
之前,我没有机会在任何地方接触、检查或修改此类请求。此类请求的唯一上下文链接是它们的NSURLRequest mainDocumentURL
。
如果有某种方法可以将原始NSURL
用作mainDocumentURL
那将是理想的。如果没有办法防止它被复制,我想到以下黑客作为替代方案:
在创建每个UIWebView
之前,将用户代理字符串设置为唯一值。据推测,此更改仅影响随后创建的UIWebView
对象,因此每个视图最终都会以自己独特的用户代理字符串结束。
在NSURLProtocol
实现中,您可以检查用户代理字符串以标识关联的UIWebView
并使用实际的用户代理字符串将其传递到实际协议处理程序(因此服务器不会看到任何不同)。
所有这些都取决于视图是否真正以不同的 UA 字符串结束。如果您设法让它工作,请告诉我!
你说你不能把它放在NSURLRequest
上,但我不清楚为什么从你更新的讨论中。这将是最自然的地方。
- 实施
webView:shouldLoadWithRequest:navigationType:
. - 使用
objc_setAssociatedObject
将额外的属性附加到提供的请求。然后返回YES
.(在这里使用setProperty:forKey:inRequest:
会很好,但是UIWebView
向我们传递了一个不可变的请求,因此我们只能附加关联的对象。UIWebView
的另一种方式是OS X的苍白阴影WebView
,它可以处理这个问题)。 - 在
NSProtocol
中,使用objc_getAssociatedObject
读取您的额外属性。该请求应与您之前提出的请求相同。你认为情况并非如此。你是说webView:shouldLoadWithRequest:navigationType:
的请求与initWithRequest:cachedResponse:client:
的请求不同吗?
我是否缺少其他要求或怪癖?
您可以通过自定义请求标头传递选项,假设目标网站或服务提供商不会以某种方式剥离传输中的选项。
挑战在于提出一种编码方案,该方案可以合理地编码为标头字段值的 ASCII 字符串,然后解码为您想要的实际值。为此,自定义NSValueTransformer
似乎是最合适的。
我遇到了同样的问题。我最终坚持了 Matthew 建议的解决方案(使用用户代理字符串)。但是,由于解决方案没有充实,我添加了包含更多详细信息的新答案。此外,我发现您无需发送请求即可使用户代理"坚持"。按照此处的建议通过javascript获取就足够了。
以下步骤对我有用:
(1) 获取当前默认用户代理。稍后需要它将其放回 NSURLProtocol 中的请求中。您需要使用新的 Web 视图,因为获取用户代理将使其粘附在 Web 视图上,因此您以后无法更改它。
UIWebView* myWebview = [[UIWebView alloc] init];
NSString* defaultUserAgent = [myWebview stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
[myWebview release]; // no needed with ARC, but to emphasize, that the webview instance is not needed anymore
(2) 更改标准用户默认值中的值(从此处获取)。
NSDictionary* userAgentDict = [NSDictionary dictionaryWithObjectsAndKeys:@"yourUserAgent", @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:userAgentDict];
(3)通过javascript获取新的用户代理字符串,使它粘在你的webView上,就像在(1)中所做的那样,但这次是在你实际使用的webview实例上。
(4) 恢复标准用户默认值中的默认用户代理,如此处所示。