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
當NSURLConnection準備發起請求時,它會遍歷所有已注冊的NSURLProtocol,詢問它們能否處理當前請求。所以你需要盡早注冊這個Protocol。
2.對于NSURLSession的請求,注冊NSURLProtocol的方式稍有不同,是通過NSURLSessionConfiguration注冊的:
3. 請求結束后注銷NSURLProtocol
[NSURLProtocol unregisterClass:[MyURLProtocol class]];4.實現NSURLProtocol的相關方法
(1)當遍歷到我們自定義的NSURLProtocol時,系統先會調用canInitWithRequest:這個方法。顧名思義,這是整個流程的入口,只有這個方法返回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。在我們的場景中推薦實現所有協議。
四、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.崩潰報錯:
有一點蘋果說明的不是很清楚,蘋果自己實現CustomHTTPProtocol源碼中很好的體現了這一點:
NSURLProtocolClient回調動作必須跟請求的托管發送保持在一個線程、相同的Runloop,具體實現邏輯如下:
(1)在start方法中記錄當前線程和Runloop模式;
(2)所有對于NSURLProtocolClient的回調,都在記錄的線程、以相同的Runloop模式觸發,使用如下方法:
坑4:httpBody
NSURLProtocol在攔截NSURLSession的POST請求時不能獲取到Request中的HTTPBody。蘋果官方的解釋是Body是NSData類型,而且還沒有大小限制。為了性能考慮,攔截時就沒有拷貝。
?
坑5:Protocol請求攔截對證書認證方法的影響
因為URLConnection新增了證書認證方法:
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;但是NSURLProtocolClient并沒有增加對應的回調方法,會導致原始請求的證書校驗代理方法不調用。
暫時無正解。有解決方案的朋友歡迎騷擾。
總結
以上是生活随笔為你收集整理的NSURLProtocol概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gtx1660是什么级别的_GTX166
- 下一篇: java中使用lua操作redis