一、概念

NSURLProtocol也是苹果众多黑魔法中的一种,使用它可以轻松地重定义整个URL Loading System。当你注册自定义NSURLProtocol后,就有机会对所有的请求进行统一的处理,基于这一点它可以让你:
1.自定义请求和响应
2.提供自定义的全局缓存支持
3.重定向网络请求
4.提供HTTP Mocking (方便前期测试)
5.其他一些全局的网络请求修改需求

二、如果注册多个URLProtocol会怎么样?

1.最后注册的Protocol最先执行。即倒序遍历。

2.如果其中一个Protocol的canInitWithRequest方法返回了YES,则后续的Protocol不再执行;否则会一直遍历,直到找到能处理此请求的Protocol。

三、使用方法

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];  

3. 请求结束后注销NSURLProtocol

[NSURLProtocol unregisterClass:[MyURLProtocol class]];  

4.实现NSURLProtocol的相关方法

(1)当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。我们可以在这个方法的实现里面进行请求的过滤,筛选出需要进行处理的请求。

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{if ([NSURLProtocol propertyForKey:MyURLProtocolHandled inRequest:request]){return NO;}if (![scheme hasPrefix:@"http"]){return NO;}return YES;
}

(2)当筛选出需要处理的请求后,就可以进行后续的处理,需要至少实现如下4个方法

+ (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。在我们的场景中推荐实现所有协议。

- (nullable NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(nullable NSURLResponse *)response
{if (response != nil){[[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];}return request;
}- (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];
}- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{[self.client URLProtocolDidFinishLoading:self];
}- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{[self.client URLProtocol:self didFailWithError:error];
}

四、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.崩溃报错:

0    libobjc.A.dylib objc_msgSend + 16
1   CFNetwork       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:Protocol请求拦截对证书认证方法的影响

因为URLConnection新增了证书认证方法:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

但是NSURLProtocolClient并没有增加对应的回调方法,会导致原始请求的证书校验代理方法不调用。

暂时无正解。有解决方案的朋友欢迎骚扰。

NSURLProtocol概述相关推荐

  1. WKWebView概述

    一.概述 1.iOS 8 SDK中发布了新的WebView框架----WebKit.framework. 2.WebKit使用WKWebView来代替IOS的UIWebView和OSX的NSWebVi ...

  2. Java 多线程概述

    多线程技术概述 1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间. 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换.并发执行,一个进程最少有一个线程, ...

  3. 【SpringMVC】概述

    概述: SpringMVC:是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的.                       理解是servlet的一个升级 Sp ...

  4. 梯度下降优化算法概述

    本文原文是 An overview of gradient descent optimization algorithms,同时作者也在 arXiv 上发了一篇同样内容的 论文. 本文结合了两者来翻译 ...

  5. Redis概述和基础

    Redis 1.NoSQL NoSQL = Not Only SQL(不仅仅是SQL) 泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的 ...

  6. OpenCL™(开放计算语言)概述

    OpenCL™(开放计算语言)概述 异构系统并行编程的开准 OpenCL™(开放计算语言)是一种开放的.免版税的标准,用于对超级计算机.云服务器.个人计算机.移动设备和嵌入式平台中的,各种加速器进行跨 ...

  7. 自动驾驶QNX,Linux,Autosar概述

    自动驾驶QNX,Linux,Autosar概述 QNX是一个分布式.嵌入式.可规模扩展的实时操作系统.遵循POSIX.1 (程序接口)和POSIX.2 (Shell和工具).部分遵循POSIX.1b( ...

  8. Tengine MLOps概述

    Tengine MLOps概述 大幅提高产业应用从云向边缘迁移的效率 MLOps Cloud Native 聚焦于提升云端的运营过程效率 MLOps Edge Native 聚焦于解决边缘应用开发及异 ...

  9. Tengine Web服务器概述

    Tengine Web服务器概述 Tengine是由淘宝网发起的Web服务器项目.在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.目的是打造一个高效.安全的Web平台. 发展 ...

最新文章

  1. MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)
  2. node-mongo封装
  3. 从eclipse中下载到手机的android程序总是旧的版本
  4. 推荐一个网站拿下机器学习优质资源!搜索效率极大提高
  5. CentOS7升级版本
  6. python定时发送邮件
  7. 个人成长:2021年终记
  8. 剑指 Offer 16. 数值的整数次方
  9. 基于JAVA+SpringMVC+Mybatis+MYSQL的外卖点餐系统
  10. PyTorch——Ubuntu上Pytorch的安装教程
  11. [Java] 蓝桥杯BASIC-28 基础练习 Huffuman树
  12. [BZOJ2339][HNOI2011]卡农
  13. 在线选课网站用什么服务器,高校网上选课系统 PC服务器替代小型机
  14. cacti 模版大全
  15. poj1273:Drainage Ditches
  16. Flash Player去广告下载地址
  17. 织梦域名后缀.html,织梦cms建站教程之首页域名后缀index.html去除的方法
  18. kafka的epoch
  19. Weakly Supervised Video Salient Object Detection
  20. 《线性代数》随笔:积沙成塔

热门文章

  1. oracle Sql语句分类
  2. vue中如何创建组件?
  3. java.lang.UnsupportedClassVersionError: Bad version number in .class file异常
  4. mac下配置eclipse的maven环境
  5. 自学php【二】 PHP计算时间加一天
  6. 20140625 程序 进程 线程 物理存储器
  7. 数据库基础知识——视图
  8. 【剑指offer】面试题32 - I:从上到下打印二叉树(Java)
  9. MVC原理及案例分析
  10. erp实施 数据库面试题_ERP管理系统多少钱