iOS:将自定义 NSURL 传递给 NSURLProtocol



我需要传递一些额外的信息以及UIWebView loadRequest:,以便它到达我的NSURLProtocol实现。信息不能绑定到NSURLRequest因为信息也必须与NSURLRequest mainDocumentURL一起保留。所以我对NSURL进行了子类化,并用它构建了NSURLRequest。我已经知道到达NSURLProtocol startLoadingNSURLRequest不是我提供给UIWebView loadRequest的实例,所以我也实现了NSURL copyWithZone,天真地期望URL加载系统会使用它。

现在,NSURLProtocol canInitWithRequest不是像人们合理预期的那样被调用一次,而是在startLoading之前至少调用 4 次。前 2 次,传入NSURLRequest仍然包含我的自定义NSURL实现。然后一个名为 CFURLCopyAbsoluteURL 的不幸内部代码要求absoluteURL我的自定义NSURL,下一个canInitWithRequest(以及随后的startLoading)已经得到了一个全新的NSURLRequest,其中包含新鲜的NSURLcopyWithZone永远不会被调用,我的子类化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) 恢复标准用户默认值中的默认用户代理,如此处所示。

最新更新