docs: refine

This commit is contained in:
LiuBinPeng
2022-04-19 17:15:49 +08:00
parent e2871d54e4
commit 7241220c8e
92 changed files with 10837 additions and 1963 deletions

View File

@@ -16,14 +16,12 @@ NSURLProtocol 是 Foundation 框架中 URL Loading System 的一部分。它可
答案就是通过子类化来定义新的或是已经存在的 URL 加载行为。如果当前的网络请求是可以被拦截的,那么开发者只需要将一个自定义的 NSURLProtocol 子类注册到 App 中,在这个子类中就可以拦截到所有请求并进行修改。
## 二、NSURLProtocol 使用场景
### 1. 技术层面
NSURLProtocol 是 URL Loading System 的一部分,所以它可以拦截所有基于 URL Loading System 的网络请求:
- NSURLSession
- NSURLConnection
- NSURLDownload
@@ -31,9 +29,10 @@ NSURLProtocol 是 URL Loading System 的一部分,所以它可以拦截所有
- NSHTTPURLResponse
- NSURLRequest
- NSMutableURLRequest
所以,基础这些基础技术开发的网络框架比如 AFNetworking、Alamofire 也可以拦截。
所以,基础这些基础技术开发的网络框架比如 AFNetworking、Alamofire 也可以拦截。
想到了2种场景不能拦截
- 早期使用 CFNetwork 实现的 ASIHTTPRequest 框架就无法拦截
- UIWebView 也是可以被拦截的。但是 WKWebView 是基于 webkit不走底层 c socket。
@@ -54,9 +53,6 @@ NSURLProtocol 是 URL Loading System 的一部分,所以它可以拦截所有
- 自定义网络请求,过滤垃圾内容
- H5 加速,请求走本地离线包
## 三、NSURLProtocol 的相关方法
创建协议对象
@@ -69,6 +65,7 @@ NSURLProtocol 是 URL Loading System 的一部分,所以它可以拦截所有
```
注册和注销协议类
```Objective-c
// 尝试注册 NSURLProtocol 的子类,使之在 URL 加载系统中可见
+ (BOOL)registerClass:(Class)protocolClass;
@@ -103,6 +100,7 @@ NSURLProtocol 允许开发者去获取、添加、删除 request 对象的任意
提供请求的规范版本
如果你想要用特定的某个方式来修改请求,可以用下面这个方法。
```Objective-c
// 返回指定请求的规范版本
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
@@ -118,6 +116,7 @@ NSURLProtocol 允许开发者去获取、添加、删除 request 对象的任意
启动和停止加载
这是子类中最重要的两个方法,不同的自定义子类在调用这两个方法时会传入不同的内容,但共同点都是围绕 protocol 客户端进行操作。
```Objective-c
// 开始加载
- (void)startLoading;
@@ -138,24 +137,19 @@ NSURLProtocol 允许开发者去获取、添加、删除 request 对象的任意
- (NSURLSessionTask *)task;
```
## 四、 如何利用 NSProtocol 拦截网络请求
NSURLProtocol 在实际应用中,主要是完成两步:拦截 URL 和 URL 转发。先来看如何拦截网络请求。
**创建 NSURLProtocol 子类**
这里创建一个名为 HTCustomURLProtocol 的子类。
```Objective-c
@interface HTCustomURLProtocol : NSURLProtocol
@end
```
**注册 NSURLProtocol 的子类**
在合适的位置注册这个子类。对基于 NSURLConnection 或者使用 [NSURLSession sharedSession] 初始化对象创建的网络请求,调用 registerClass 方法即可。
@@ -166,16 +160,12 @@ NSURLProtocol 在实际应用中,主要是完成两步:拦截 URL 和 URL
// [NSURLProtocol registerClass:[HTCustomURLProtocol class]];
```
如果需要全局监听,可以设置在 `AppDelegate.m` 的 `didFinishLaunchingWithOptions` 方法中。如果只需要在单个 UIViewController 中使用,记得在合适的时机注销监听:
```objective-c
[NSURLProtocol unregisterClass:[NSClassFromString(@"HTCustomURLProtocol") class]];
```
如果是基于 `NSURLSession` 的网络请求,且不是通过 `[NSURLSession sharedSession]` 方式创建的,就得配置 `NSURLSessionConfiguration` 对象的 `protocolClasses` 属性。
```objective-c
@@ -184,16 +174,10 @@ NSURLProtocol 在实际应用中,主要是完成两步:拦截 URL 和 URL
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
```
**实现 NSURLProtocol 子类**
> 注册 → 拦截 → 转发 → 回调 → 结束
以拦截 UIWebView 为例,这里需要重写父类的这五个核心方法。
```objective-c
@@ -323,11 +307,8 @@ didCancelAuthenticationChallenge:challenge];
}
```
注意NSURLConnection 已经被废弃,推荐使用 NSURLSession 进行网络请求,它好处多多,具体的自行查阅官方介绍。
## 五、 读源码,学习 NSURLProtocol
iOS 中网络测试框架 [ OHHTTPStubs](https://github.com/AliSoftware/OHHTTPStubs)的实现就是利用了 NSURLProtocol 实现的。
@@ -349,15 +330,8 @@ HTTPStubsProtocol 继承自 NSURLProtocol可以在 HTTP 请求发送之前对
`firstStubPassingTestForRequest` 方法内部会判断请求是否需要被当前对象处理
紧接着开始发送网络请求。实际上在 `- (void)startLoading` 方法中可以用任何网络能力去完成请求,比如 NSURLSession、NSURLConnection、AFNetworking 或其他网络框架。OHHTTPStubs 的做法是获取 request、client 对象。如果 HTTPStubs 单例中包含 `onStubActivationBlock` 对象,则执行该 block然后利用 responseBlock 对象返回一个 HTTPStubsResponse 响应对象。
## 六、 补充内容
### 1. 使用 NSURLSession 时的注意事项
@@ -368,16 +342,12 @@ HTTPStubsProtocol 继承自 NSURLProtocol可以在 HTTP 请求发送之前对
• 如果要用 registerClass 注册,只能通过 ` [NSURLSession sharedSession] `的方式创建网络请求。
### 2. 注册多个 NSURLProtocol 子类
当有多个自定义 NSURLProtocol 子类注册到系统中的话,会按照他们注册的反向顺序依次调用 URL 加载流程,也就是最后注册的 NSURLProtocol 会被优先判断。
对于通过配置 NSURLSessionConfiguration 对象的 protocolClasses 属性来注册的情况protocolClasses 数组中只有第一个 NSURLProtocol 会起作用,后续的 NSURLProtocol 就无法拦截到了。
### 3. 如何拦截 WKWebview
WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。苹果开源的 webKit2 源码暴露了 私有API
@@ -402,15 +372,15 @@ if ([cls respondsToSelector:sel]) {
该方案还存在2个严重缺陷
1. post 请求 body 数据被清空
由于 WKWebview 在独立的进程中执行网络请求,一旦注册 registerSchemeForCustomProtocol httphttps scheme 后,网络请求将从 Network Process 发送到 App Process这样 NSURLProtocol 才能拦截网络请求。在 Webkit2 的设计里使用 **MessageQueue** 进行进程间通信, Network Process 会将请求 encode 成一个 message然后通过 IPC 发送给 App Process出于性能角度的考虑encode 的时候 HTTPBody 和 HTTPBodyStream 这2个字段被丢弃掉了。可以查看 [webkit2 源码](https://github.com/WebKit/webkit/blob/fe39539b83d28751e86077b173abd5b7872ce3f9/Source/WebKit2/Shared/mac/WebCoreArgumentCodersMac.mm#L61-L88)。
2. 对 ATS 支持不足
info.plist 中打开 ATS 开关,设置 Allow Arbitrary Loads 选项为 NO设置 registerSchemeForCustomProtocol 注册了 httphttps schemeWKWebView 发起的所有 http 网络请求将被阻塞(即使 Allow Arbitrary Loads in Web Content 选项为 YES
WKWebView 可以注册 customScheme比如自定义 scheme`Hybrid://`,因此使用离线包但不使用 post 方式的请求可以通过 customScheme 发起。比如 `Hybrid://www.xxx.com/`,然后在 App 进程被 NSURLProtocol 拦截这个请求,然后加载离线包资源。
不足:使用 post 方式的请求需要修改 h5 侧代码scheme
### 4. WKWebView loadRequest 问题
@@ -440,34 +410,9 @@ if ([cls respondsToSelector:sel]) {
6. 网络请求完成后,通过 NetworkProtocolClient 将请求结果返回给 WKWebView。
### 5. 拦截 WebView 内 Ajax 请求
其实上述的方法也是可行,不过使用私有 API 的方式不是很推荐,一般在穷途末路的时候才选择私有 API所以另一种思路是 hook Web 端的 ajax 请求。在执行 hook 后的 ajax 请求的时候将 ajax 的请求相关信息请求方式、header、body 等)以 messageHandler 的方式告诉 Native然后起到监控的效果。
参考: https://www.jianshu.com/p/7337ac624b8ehttps://github.com/wendux/Ajax-hook
1.
关于 WKWebview 的各种问题可以查看这篇[文章](https://www.tuicool.com/articles/QbE3Mb7)。
1. 关于 WKWebview 的各种问题可以查看这篇[文章](https://www.tuicool.com/articles/QbE3Mb7)。