iOS 開發之— NSURLProtocol

發表於2016-10-10

最近在專案裡由於電信那邊發生dns發生域名劫持,因此需要手動將URL請求的域名重定向到指定的IP地址,但是由於請求可能是通過NSURLConnection,NSURLSession或者AFNetworking等方式,因此要想統一進行處理,一開始是想通過Method Swizzling去hook cfnetworking底層方法,後來發現其實有個更好的方法–NSURLProtocol。

NSURLProtocol

NSURLProtocol能夠讓你去重新定義蘋果的URL載入系統 (URL Loading System)的行為,URL Loading System裡有許多類用於處理URL請求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSession等,當URL Loading System使用NSURLRequest去獲取資源的時候,它會建立一個NSURLProtocol子類的例項,你不應該直接例項化一個NSURLProtocol,NSURLProtocol看起來像是一個協議,但其實這是一個類,而且必須使用該類的子類,並且需要被註冊。

使用場景

不管你是通過UIWebView, NSURLConnection 或者第三方庫 (AFNetworking, MKNetworkKit等),他們都是基於NSURLConnection或者 NSURLSession實現的,因此你可以通過NSURLProtocol做自定義的操作。

  • 重定向網路請求
  • 忽略網路請求,使用本地快取
  • 自定義網路請求的返回結果
  • 一些全域性的網路請求設定

攔截網路請求

子類化NSURLProtocol並註冊

然後在application:didFinishLaunchingWithOptions:方法中註冊該CustomURLProtocol,一旦註冊完畢後,它就有機會來處理所有交付給URL Loading system的網路請求。

實現CustomURLProtocol

註冊好了之後,現在可以開始實現NSURLProtocol的一些方法:

  • +canInitWithRequest:
    這個方法主要是說明你是否打算處理對應的request,如果不打算處理,返回NO,URL Loading System會使用系統預設的行為去處理;如果打算處理,返回YES,然後你就需要處理該請求的所有東西,包括獲取請求資料並返回給 URL Loading System。網路資料可以簡單的通過NSURLConnection去獲取,而且每個NSURLProtocol物件都有一個NSURLProtocolClient例項,可以通過該client將獲取到的資料返回給URL Loading System。
    這裡有個需要注意的地方,想象一下,當你去載入一個URL資源的時候,URL Loading System會詢問CustomURLProtocol是否能處理該請求,你返回YES,然後URL Loading System會建立一個CustomURLProtocol例項然後呼叫NSURLConnection去獲取資料,然而這也會呼叫URL Loading System,而你在+canInitWithRequest:中又總是返回YES,這樣URL Loading System又會建立一個CustomURLProtocol例項導致無限迴圈。我們應該保證每個request只被處理一次,可以通過+setProperty:forKey:inRequest:標示那些已經處理過的request,然後在+canInitWithRequest:中查詢該request是否已經處理過了,如果是則返回NO。

  • +canonicalRequestForRequest:
    通常該方法你可以簡單的直接返回request,但也可以在這裡修改request,比如新增header,修改host等,並返回一個新的request,這是一個抽象方法,子類必須實現。

  • +requestIsCacheEquivalent:toRequest:
    主要判斷兩個request是否相同,如果相同的話可以使用快取資料,通常只需要呼叫父類的實現。

  • -startLoading -stopLoading
    這兩個方法主要是開始和取消相應的request,而且需要標示那些已經處理過的request。

  • NSURLConnectionDataDelegate方法
    在處理網路請求的時候會呼叫到該代理方法,我們需要將收到的訊息通過client返回給URL Loading System。

現在你已經可以擷取request並做你想做的事了,這裡有個demo可以參考一下,擷取request並重新定向到新的地址,具體dns解析方法可以參看DNS解析) ,如有不對,歡迎指正,哈~

相關文章