NSURLProtocol

研究一下NSURLProtocol.h。

NSURLProtocol是什么?

NSURLProtocol是URL Loading System的重要组成部分。
首先虽然名叫NSURLProtocol,但它却不是协议。它是一个抽象类。我们要使用它的时候需要创建它的一个子类。
NSURLProtocol在iOS系统中大概处于这样一个位置。

如果开发者自定义的一个NSURLProtocol并且注册到app中,那么在这个自定义的NSURLProtocol中我们可以拦截UIWebView,基于系统的NSURLConnection或者NSURLSession进行封装的网络请求,然后做到自定义的response返回。非常强大。

NSURLProtocol能拦截哪些网络请求

URL Loading System


所以,可以拦截的网络请求包括NSURLSession,NSURLConnection以及UIWebVIew。
基于CFNetwork的网络请求,以及WKWebView的请求是无法拦截的。

现在主流的iOS网络库,例如AFNetworking,Alamofire等网络库都是基于NSURLSession或NSURLConnection的,所以这些网络库的网络请求都可以被NSURLProtocol所拦截 。
还有一些年代比较久远的网络库,例如ASIHTTPRequest,MKNetwokit等网路库都是基于CFNetwork的,所以这些网络库的网络请求无法被NSURLProtocol拦截。

如何使用

NSURLProtocol是一个抽象类。我们要使用它的时候需要创建它的一个子类。
@interface PLHHTTPProtocol : NSURLProtocol
使用NSURLProtocol的主要可以分为5个步骤:
注册—>拦截—>转发—>回调—>结束

注册:

在系统加载的时候,把自定义的PLHHTTPProtocol注册到URL加载系统中,这样 所有的URL请求都有机会进入我们自定义的PLHHTTPProtocol进行拦截处理。

1
2
3
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[NSURLProtocol registerClass:[PLHHTTPURLProtocol class]];
}

对于基于NSURLSession的网络请求,需要通过配置NSURLSessionConfiguration对象的protocolClasses属性。

1
2
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.protocolClasses = @[[NSClassFromString(@"CustomURLProtocol") class]];

拦截:

在拦截到网络请求后,NSURLProtocol会依次执行下列方法:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request

该方法会拿到request的对象,我们可以通过该方法的返回值来筛选request是否需要被NSURLProtocol做拦截处理。
如果返回YES则进入该自定义加载器进行处理,如果返回NO则不进入该自定义选择器,使用系统默认行为进行处理。

重新设置NSURLRequest的信息:
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
比如请求重定向或者添加头部信息等等。如果没有特殊需求,直接返回request就可以了。但是因为这个方法在会在一次请求中被调用多次(暂时我也不知道什么原因为什么需要回调多洗),所以request重定向和添加头部信息也可以在开始加载中startLoading方法中重新设置。

转发

在拦截到网络请求,并且对网络请求进行定制处理以后。我们需要将网络请求重新发送出去。

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client

该方法会创建一个NSURLProtocol实例,这里每一个网络请求都会创建一个新的实例。被拦截的请求开始执行的地方.

- (void)startLoading

接下来就是转发的核心方法startLoading。在该方法中,我们把处理过的request重新发送出去。至于发送的形式,可以是基于NSURLConnection,NSURLSession甚至CFNetwork。

回调

既是面向切面的编程,就不能影响到原来网络请求的逻辑。所以上一步将网络请求转发出去以后,当收到网络请求的返回,还需要再将返回值返回给原来发送网络请求的地方。
主要需要需要调用到

1
2
3
4
[self.client URLProtocol:self didFailWithError:error];
[self.client URLProtocolDidFinishLoading:self];
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocol:self didLoadData:data];

这四个方法来回调给原来发送网络请求的地方。
这里假设我们在转发过程中是使用NSURLSession发送的网络请求,那么在NSURLSession的回调方法中,我们做相应的处理即可。并且我们也可以对这些返回,进行定制化处理。

结束:

在一个网络请求完全结束以后,NSURLProtocol回调用到.结束加载URL请求
- (void)stopLoading

应用

既然NSURLProtocol功能非常强大,那么在具体开发中,会有哪些应用呢?

  • 自定义请求和响应
  • 网络的缓存处理(H5离线包 和 网络图片缓存)
  • 重定向网络请求
  • 为测试提供数据Mocking功能,在没有网络的情况下使用本地数据返回。
  • 过滤掉一些非法请求
  • 快速进行测试环境的切换
  • 拦截图片加载请求,转为从本地文件加载
  • 可以拦截UIWebView,基于系统的NSURLConnection或者NSURLSession进行封装的网络请求。目前WKWebView无法被NSURLProtocol拦截。
  • 当有多个自定义NSURLProtocol注册到系统中的话,会按照他们注册的反向顺序依次调用URL加载流程。当其中有一个NSURLProtocol拦截到请求的话,后续的NSURLProtocol就无法拦截到该请求。

DEMO地址:https://github.com/peilinghui/BokeDemo/tree/master/PLHHTTPDNSDemo

参考

NSURLProtocol全攻略
WKWebView 不支持 NSURLProtocol 吗

文章目录
  1. 1. NSURLProtocol是什么?
  2. 2. NSURLProtocol能拦截哪些网络请求
  3. 3. 如何使用
    1. 3.1. 注册:
    2. 3.2. 拦截:
    3. 3.3. 转发
    4. 3.4. 回调
    5. 3.5. 结束:
  4. 4. 应用
  5. 5. 参考
本站总访问量 本站访客数人次 ,