攔截原理:
- NSURLProtocol 是蘋果為我們提供的 URL Loading System 的一部分,在每一個(gè) HTTP 請(qǐng)求開始時(shí)吱涉,URL 加載系統(tǒng)創(chuàng)建一個(gè)合適的 NSURLProtocol 對(duì)象處理對(duì)應(yīng)的 URL 請(qǐng)求刹泄,而我們需要做的就是寫一個(gè)繼承自 NSURLProtocol 的類,并通過 - registerClass: 方法注冊(cè)我們的協(xié)議類怎爵,然后 URL 加載系統(tǒng)就會(huì)在請(qǐng)求發(fā)出時(shí)使用我們創(chuàng)建的協(xié)議對(duì)象對(duì)該請(qǐng)求進(jìn)行處理特石。
// 1.注冊(cè)我們的協(xié)議類
[NSURLProtocol registerClass:[DLCustomURLProtocol class]];
### 攔截方法的介紹:
// 2.是否能夠處理給定的請(qǐng)求
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
// 3.處理URL轉(zhuǎn)換為IP地址
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
// 4.開始網(wǎng)絡(luò)請(qǐng)求
- (void)startLoading;
// 5.結(jié)束網(wǎng)絡(luò)請(qǐng)求
- (void)stopLoading;
// 6.處理網(wǎng)絡(luò)請(qǐng)求的code
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
// 7.處理網(wǎng)絡(luò)請(qǐng)求的返回信息
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
WKWebView使用私有API的原因:
- WKURLSchemeHandler是iOS11就推出的,用于處理自定義請(qǐng)求的方案鳖链,不過并不能處理HTTP姆蘸、HTTPS等常規(guī)scheme。但是現(xiàn)在有另外一個(gè)方法芙委,就是去 WKWebview的initWithFrame:方法逞敷,然后設(shè)置一個(gè)WKURLSchemeHandler給WKWebview實(shí)例,這樣就能截獲WKWebview的HTTP和HTTPS請(qǐng)求了灌侣。
WebKit 進(jìn)程是獨(dú)立于 app 進(jìn)程之外的推捐,兩個(gè)進(jìn)程之間使用消息隊(duì)列的方式進(jìn)行進(jìn)程間通信。比如 app 想使用 WKWebView 加載一個(gè)請(qǐng)求侧啼,就要把請(qǐng)求的參數(shù)打包成一個(gè) Message牛柒,然后通過 IPC 把 Message 交給 WebKit 去加載堪簿,反過來 WebKit 的請(qǐng)求想傳到 app 進(jìn)程的話(比如 URLProtocol ),也要打包成 Message 走 IPC焰络。出于性能的原因戴甩,打包的時(shí)候 HTTPBody 和 HTTPBodyStream 這兩個(gè)字段被丟棄掉了,這個(gè)可以參考 WebKit 的源碼闪彼,這就導(dǎo)致 -[WKWebView loadRequest:] 傳出的 HTTPBody 和 NSURLProtocol 傳回的 HTTPBody 全都被丟棄掉了甜孤。所以如果通過 NSURLProtocol 注冊(cè)攔截 http scheme,那么由 WebKit 發(fā)起的所有 http POST 請(qǐng)求就全都無效了.
#pragma mark - WKWebView的私有API
#pragma mark - 以下方法為iOS的私有方法畏腕,建議上線時(shí)注釋————以下全部代碼
+ (void)DL_unregisterScheme {
[self wk_registerScheme:@"http"];
[self wk_registerScheme:@"https"];
}
FOUNDATION_STATIC_INLINE Class ContextControllerClass() {
static Class cls;
if (!cls) {
cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
}
return cls;
}
FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() {
return NSSelectorFromString(@"registerSchemeForCustomProtocol:");
}
+ (void)wk_registerScheme:(NSString *)scheme {
Class cls = ContextControllerClass();
SEL sel = RegisterSchemeSelector();
if ([(id)cls respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
}
}
抓取第三方AFN請(qǐng)求
- 對(duì)于NSURLSession的網(wǎng)絡(luò)請(qǐng)求缴川,需要替換protocolClasses方法
+ (void)exchangeAFNSessionConfiguration {
Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
Method originalMethod = class_getInstanceMethod(cls, @selector(protocolClasses));
Method stubMethod = class_getInstanceMethod([DLCustomURLProtocol class], @selector(protocolClasses));
if (!originalMethod || !stubMethod) {
CLog(@"Couldn't load NEURLSessionConfiguration");
return;
}
method_exchangeImplementations(originalMethod, stubMethod);
}