NSURLProtocol概述
来源:互联网 发布:用java编写计算器程序 编辑:程序博客网 时间:2024/05/17 03:27
一、概念
NSURLProtocol也是苹果众多黑魔法中的一种,使用它可以轻松地重定义整个URL Loading System。当你注册自定义NSURLProtocol后,就有机会对所有的请求进行统一的处理,基于这一点它可以让你:
1.自定义请求和响应
2.提供自定义的全局缓存支持
3.重定向网络请求
4.提供HTTP Mocking (方便前期测试)
5.其他一些全局的网络请求修改需求
二、使用方法
1.继承NSURLPorotocl,并注册你的NSURLProtocol
[NSURLProtocol registerClass:[MyURLProtocol class]];当NSURLConnection准备发起请求时,它会遍历所有已注册的NSURLProtocol,询问它们能否处理当前请求。所以你需要尽早注册这个Protocol。
2.对于NSURLSession的请求,注册NSURLProtocol的方式稍有不同,是通过NSURLSessionConfiguration注册的:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSArray *protocolArray = @[ [MyURLProtocol class] ]; configuration.protocolClasses = protocolArray; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; NSURLSessionTask *task = [session dataTaskWithRequest:request]; [task resume];
[NSURLProtocol unregisterClass:[MyURLProtocol class]];
(1)当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。我们可以在这个方法的实现里面进行请求的过滤,筛选出需要进行处理的请求。
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{ if ([NSURLProtocol propertyForKey:MyURLProtocolHandled inRequest:request]) { return NO; } if (![scheme hasPrefix:@"http"]) { return NO; } return YES;}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{ return request;}+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{ return [super requestIsCacheEquivalent:a toRequest:b];}- (void)startLoading{ NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy]; [NSURLProtocol setProperty:@(YES) forKey:MyURLProtocolHandled inRequest:mutableReqeust]; self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];}- (void)stopLoading{ [self.connection cancel]; self.connection = nil;}
说明:
(1)canonicalRequestForRequest: 返回规范化后的request,一般就只是返回当前request即可。
(2)requestIsCacheEquivalent:toRequest: 用于判断你的自定义reqeust是否相同,这里返回默认实现即可。它的主要应用场景是某些直接使用缓存而非再次请求网络的地方。
(3)startLoading和stopLoading 实现请求和取消流程。
5.实现NSURLConnectionDelegate和NSURLConnectionDataDelegate
因为在第二步中我们接管了整个请求过程,所以需要实现相应的协议并使用NSURLProtocolClient将消息回传给URL Loading System。在我们的场景中推荐实现所有协议。
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ [self.client URLProtocol:self didFailWithError:error];}- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response{ if (response != nil) { [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; } return request;}- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{ return YES;}- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ [self.client URLProtocol:self didReceiveAuthenticationChallenge:challenge];}- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { [self.client URLProtocol:self didCancelAuthenticationChallenge:challenge];}- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:[[self request] cachePolicy]];}- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [self.client URLProtocol:self didLoadData:data];}- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse{ return cachedResponse;}- (void)connectionDidFinishLoading:(NSURLConnection *)connection{ [self.client URLProtocolDidFinishLoading:self];}
三、NSURLProtocol那些坑
坑1:企图在canonicalRequestForRequest:进行request的自定义操作,导致各种递归调用导致连接超时。这个API的表述其实很暧昧:
It is up to each concrete protocol implementation to define what “canonical” means. A protocol should guarantee that the same input request always yields the same canonical form.
所谓的canonical form到底是什么呢?而围观了包括NSEtcHosts和RNCachingURLProtocol在内的实现,它们都是直接返回当前request。在这个方法内进行request的修改非常容易导致递归调用(即使通过setProperty:forKey:inRequest:对请求打了标记)
坑2:没有实现足够的回调方法导致各种奇葩问题。如connection:willSendRequest:redirectResponse: 内如果没有通过[self client]回传消息,那么需要重定向的网页就会出现问题:host不对或者造成跨域调用导致资源无法加载。
坑3.崩溃报错:
0libobjc.A.dylib objc_msgSend + 161CFNetwork CFURLProtocol_NS::forgetProtocolClient() + 124有一点苹果说明的不是很清楚,苹果自己实现CustomHTTPProtocol源码中很好的体现了这一点:
NSURLProtocolClient回调动作必须跟请求的托管发送保持在一个线程、相同的Runloop,具体实现逻辑如下:
(1)在start方法中记录当前线程和Runloop模式;
(2)所有对于NSURLProtocolClient的回调,都在记录的线程、以相同的Runloop模式触发,使用如下方法:
[self performSelector:onThread:withObject:waitUntilDone:modes:];
坑4:httpBody
NSURLProtocol在拦截NSURLSession的POST请求时不能获取到Request中的HTTPBody。苹果官方的解释是Body是NSData类型,而且还没有大小限制。为了性能考虑,拦截时就没有拷贝。
5.如果还有什么其它问题,建议仔细看看苹果的CustomHTTPProtocol源码,应该会发现一些问题。
0 0
- NSURLProtocol概述
- NSURLProtocol
- NSURLProtocol Tutorial
- NSURLProtocol 使用
- NSURLProtocol与NSURLProtocolClient简介
- NSURLProtocol的使用
- iOS开发之--- NSURLProtocol
- iOS开发之--- NSURLProtocol
- iOS开发之--- NSURLProtocol
- iOS开发之--- NSURLProtocol
- NSURLProtocol的使用
- iOS开发之--- NSURLProtocol
- iOS开发之--- NSURLProtocol
- iOS中的NSURLProtocol
- iOS中的NSURLProtocol
- NSURLProtocol 离线缓存
- 让 WKWebView 支持 NSURLProtocol
- WKWebView实现NSURLProtocol
- iOS中NSLog的优化使用
- linux--chmod
- iOS中bundle的使用
- NSRunloop小总结
- H5离线缓存
- NSURLProtocol概述
- android Handler 机制
- 使用NSURLProtocol实现离线缓存
- WKWebView概述
- CoreData 自定义数据类型
- XMLDictionary iOS的XML处理包
- Eclipse安装WindowBuilder
- Nov 4th 开始进入正轨了
- CoreData并发操作模式简介