日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NSURLProtocol概述

發布時間:2024/7/23 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NSURLProtocol概述 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

一、概念

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概述的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。