業(yè)內已有的 WKWebView 請求攔截方案,主要分為如下兩種
1、NSURLProtocol
NSURLProtocol 默認會攔截所有經過 URL Loading System 的請求,因此只要 WKWebView 發(fā)出的請求經過 URL Loading System 就可以被攔截。經過我們的嘗試,發(fā)現 WKWebView 獨立于應用進程運行,發(fā)出去的請求默認是不會經過 URL Loading System,需要我們額外進行 hook 才能支持,具體的方式可以參考 NSURLProtocol對WKWebView的處理。
WKWebView攔截網絡請求的坑
雖然NSURLProtocol可以攔截監(jiān)聽每一個URL Loading System中發(fā)出request請求,記住是URL Loading System中那些類發(fā)出的請求,也支持AFNetwoking,UIWebView發(fā)出的request,NSURLProtocol都可以攔截和監(jiān)聽。
因為WKWebView 在獨立進程里執(zhí)行網絡請求。一旦注冊 http(s) scheme 后,網絡請求將從 Network Process 發(fā)送到 App Process,這樣 NSURLProtocol 才能攔截網絡請求。
但是在 WebKit2 的設計里使用 MessageQueue 進行進程之間的通信,Network Process 會將請求 encode 成一個 Message,然后通過 IPC(進程間通信) 發(fā)送給 App Process。出于性能的原因,encode 的時候 將HTTPBody 和 HTTPBodyStream 這兩個字段丟棄掉(坑)
因此,如果通過 registerSchemeForCustomProtocol 注冊了 http(s) scheme, 那么由 WKWebView 發(fā)起的所有 http(s)請求都會通過 IPC 傳給主進程 NSURLProtocol 處理,導致 post 請求 body 被清空;
//蘋果開源的 WebKit2 源碼暴露了私有API:
+ [WKBrowsingContextController registerSchemeForCustomProtocol:]
//通過注冊 http(s) scheme 后 WKWebView 將可以使用 NSURLProtocol 攔截 http(s) 請求:
Class cls = NSClassFromString(@"WKBrowsingContextController”);
SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
if ([(id)cls respondsToSelector:sel]) {
// 注冊http(s) scheme, 把 http和https請求交給 NSURLProtocol處理
[(id)cls performSelector:sel withObject:@"http"];
[(id)cls performSelector:sel withObject:@"https"];
}
說明1:名目張膽使用私有API,是過不了AppStore審核的,具體使用什么辦法,想來你也懂(hun xiao)。
說明2:一旦打開ATS開關:Allow Arbitrary Loads 選項設置為NO,通過 registerSchemeForCustomProtocol 注冊了 http(s) scheme,WKWebView 發(fā)起的所有 http(s) 網絡請求將被阻塞(即便將Allow Arbitrary Loads in Web Content 選項設置為YES);
說明3:iOS11之后可以通過WKURLSchemeHandler去完成對WKWebView的請求攔截,不需要再調用私有API解決上述問題了。
2、WKURLSchemeHandler
WKURLSchemeHandler是iOS11就推出的,用于處理自定義請求的方案,不過并不能處理Http、Https等常規(guī)scheme。
WKURLSchemeHandler 負責自定義請求的數據管理,如果需要支持 scheme 為 http 或 https請求的數據管理則需要 hook WKWebView 的 handlesURLScheme: 方法,然后返回NO即可。
- 隔離性:
NSURLProtocol一經注冊就是全局開啟。一般來講我們只會攔截自己的業(yè)務頁面,但使用了NSURLProtocol的方式后會導致應用內合作的三方頁面也會被攔截從而被污染。WKURLSchemeHandler則可以以頁面為維度進行隔離,因為是跟隨著WKWebViewConfiguration進行配置。 - 穩(wěn)定性:
NSURLProtocol攔截過程中會丟失 Body,WKURLSchemeHandler在 iOS 11.3 之前 (不包含) 也會丟失 Body,在 iOS 11.3 以后 WebKit 做了優(yōu)化只會丟失 Blob 類型數據。 - 一致性:
WKWebView發(fā)出的請求被NSURLProtocol攔截后行為可能發(fā)生改變,比如想取消 video 標簽的視頻加載一般都是將資源地址 (src) 設置為空,但此時stopLoading方法卻不會調用,相比而言WKURLSchemeHandler表現正常。
調研的結論是:WKURLSchemeHandler 在隔離性、穩(wěn)定性、一致性上表現優(yōu)于 NSURLProtocol,但是想在生產環(huán)境投入使用必須要解決 Body 丟失的問題。
WKWebView自定義資源scheme
- 向WKWebView 注冊 customScheme, 比如 dynamic://, 而不是https或http,避免對https或http請求的影響
- 保證使用離線包功能的請求,沒有post方式,遇到customScheme請求,比如
dynamic://www.dynamicalbumlocalimage.com/,通過 NSURLProtocol 攔截這個請求并加載離線數據。 - iOS 11上, WebKit 提供的WKURLSchemeHandler可實現攔截,需要注意的只允許開發(fā)者攔截自定義 Scheme 的請求,不允許攔截 “http”、“https”、“ftp”、“file” 等的請求,否則會crash。
總結:
NSURLProtocol
缺點:針對WKWebView流量需要額外使用私有方法,雖然可以通過混淆方式屏蔽關鍵詞但是無法保證日后版本升級仍然適用
WKURLSchemeHandler
缺點:需要針對WKWebView實例設置,WKWebView實例零散的場景不適用
引用文章: