iOS 关于重定向的那些事(NSURLProcotol-WKWebView)
重定向定义:重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。
不管你是通过UIWebView, NSURLConnection 或者第三方库 (AFNetworking, MKNetworkKit等),他们都是基于NSURLConnection或者 NSURLSession实现的,因此你可以通过NSURLProtocol做自定义的操作。
- 重定向网络请求
- 忽略网络请求,使用本地缓存
- 自定义网络请求的返回结果
- 一些全局的网络请求设置
由于公司做免流增值业务,所以对于重定向的使用比较多,当用户通过手机流量访问部分请求时,需要进行重定向,让用户走免流服务器,这样可以做到定向流量的需求。
言归正传,关于NSURLProcotol的基本使用可以参考这篇文章:http://www.jianshu.com/p/7c89b8c5482a
如果要是对于WKWebView的配置需要特别注意一下。
注意点:
- WKWebView 在独立于 App Process 进程之外的 Network Process 进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。
- 可见内部对全局的 WebProcessPool 进行了自定义 scheme 的注册和注销。
- WKBrowsingContextController 通过 registerSchemeForCustomProtocol 向 WebProcessPool 注册全局自定义 scheme
- WebProcessPool 使用已注册的 scheme 初始化 Network Process 进程配置,同时设置 CustomProtocolManager,负责把网络请求通过 IPC 发送到 App Process 进程、也接收从 App Process 进程返回的网络响应 response
- CustomProtocolManager 注册了 NSURLProtocol 的子类 WKCustomProtocol,负责拦截网络请求处理
- CustomProtocolManagerProxy 中的 WKCustomProtocolLoader 使用 NSURLConnection 发送实际的网络请求,并将响应 response 返回给 CustomProtocolManager
使用示例:对图片的处理
+ (BOOL)canInitWithRequest:(NSURLRequest *)request { NSString* extension = request.URL.pathExtension; BOOL isImage = [@[@"png", @"jpeg", @"gif", @"jpg"] indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { return [extension compare:obj options:NSCaseInsensitiveSearch] == NSOrderedSame; }] != NSNotFound; return [NSURLProtocol propertyForKey:FilteredKey inRequest:request] == nil && isImage; }
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; }
- (void)startLoading { NSMutableURLRequest* request = self.request.mutableCopy; [NSURLProtocol setProperty:@YES forKey:FilteredKey inRequest:request]; NSData* data = UIImagePNGRepresentation([UIImage imageNamed:@"image"]); NSURLResponse* response = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:@"image/png" expectedContentLength:data.length textEncodingName:nil]; [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; [self.client URLProtocol:self didLoadData:data]; [self.client URLProtocolDidFinishLoading:self]; }
重点:
进行注册和注销
//注册 + (void)wk_registerScheme:(NSString*)scheme; //注销 + (void)wk_unregisterScheme:(NSString*)scheme;
实现注册与注销:
FOUNDATION_STATIC_INLINE Class ContextControllerClass() { static Class cls; if (!cls) { cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class]; } return cls; } FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() { return NSSelectorFromString(@"registerSchemeForCustomProtocol:"); } FOUNDATION_STATIC_INLINE SEL UnregisterSchemeSelector() { return NSSelectorFromString(@"unregisterSchemeForCustomProtocol:"); } @implementation NSURLProtocol (WebKitSupport) + (void)wk_registerScheme:(NSString *)scheme { Class cls = ContextControllerClass(); SEL sel = RegisterSchemeSelector(); if ([(id)cls respondsToSelector:sel]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [(id)cls performSelector:sel withObject:scheme]; #pragma clang diagnostic pop } } + (void)wk_unregisterScheme:(NSString *)scheme { Class cls = ContextControllerClass(); SEL sel = UnregisterSchemeSelector(); if ([(id)cls respondsToSelector:sel]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [(id)cls performSelector:sel withObject:scheme]; #pragma clang diagnostic pop } }
注册内容基本已经完成,完成之后需要将在全局中注册 [NSURLProtocol registerClass:[ReplacingImageURLProtocol class]];
这样就完成了对于WKWebViewde拦截与重定向,可以通过WKNavigationDelegate的代理来获取结果:
#pragma mark - WKNavigationDelegate - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable result, NSError * _Nullable error) { if ([result isKindOfClass:[NSString class]]) { self.title = result; } }]; } #pragma mark - UIWebViewDelegate - (void)webViewDidFinishLoad:(UIWebView *)webView { self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; } #pragma mark - Getters - (UIView *)webView { if (!_webView) { _webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if ([_webView respondsToSelector:@selector(setNavigationDelegate:)]) { [_webView setNavigationDelegate:self]; } if ([_webView respondsToSelector:@selector(setDelegate:)]) { [_webView setDelegate:self]; } } return _webView; }
以上的就是对于WKWebView的处理了,对于NSURLProcotol的使用基本上就是这些,使用它可以让你在不改变链接的情况下进行重定向,也方便全局代理的使用与处理,可以极大的提高效率。有什么疑问的可以加我的扣扣:1123231279,大家可以一起探讨。