NSURLProtocol攔截網(wǎng)絡(luò)請求用法與理解

NSURLProtocol

官方解釋一個抽象類,用于處理特定于協(xié)議的URL數(shù)據(jù)的加載。

使用方法

不要NSURLProtocol直接實例化子類。而是為您的應(yīng)用支持的任何自定義協(xié)議或URL方案創(chuàng)建子類。下載開始時,系統(tǒng)會創(chuàng)建相應(yīng)的協(xié)議對象來處理相應(yīng)的URL請求。您可以在應(yīng)用程序的啟動時間內(nèi)定義協(xié)議類并調(diào)用類方法,以便系統(tǒng)了解您的協(xié)議。registerClass:

用NSURLProtocol可以統(tǒng)一處理app內(nèi)你發(fā)的協(xié)議,例如你要對請求頭進行處理加工,對請求以及響應(yīng)處理都是個不錯的地方

1.首先創(chuàng)建一個繼承于NSURLProtocol的類

#import <Foundation/Foundation.h>

@interface XiaDianProtocol : NSURLProtocol

@end

2.然后先在app開啟的時候加入如下代碼,注冊自定義協(xié)議類,這樣你發(fā)的請求都會通過你這類進行過濾在進行下一步操作

[NSURLProtocol registerClass:[XiaDianProtocol class]];
過濾網(wǎng)絡(luò)請求

3.寫一個簡單網(wǎng)絡(luò)請求,API在網(wǎng)上找的看一下效果

  //創(chuàng)建一個網(wǎng)絡(luò)路徑
    NSString *browseUrl = [NSString stringWithFormat:@"https://www.sojson.com/open/api/weather/json.shtml?city=%@", @"北京"];
    //處理一下特殊字符漢字等
    browseUrl =  [browseUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
    //創(chuàng)建一個網(wǎng)絡(luò)請求
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:browseUrl]];
    //創(chuàng)建一個Task任務(wù):
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (data == nil) {
            return ;
        }
         NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingMutableLeaves) error:nil];
        NSLog(@"從服務(wù)器獲取到數(shù)據(jù)%@", dict);
    }];
    NSLog(@"sessionDataTask------>%p", sessionDataTask);
    //執(zhí)行任務(wù)
    [sessionDataTask resume];

4.運行結(jié)果當然是崩的 因為protocol里有必須要實現(xiàn)的方法 要不崩至少要實現(xiàn)有下面幾個API

方法API 注釋
+ (BOOL)canInitWithRequest:(NSURLRequest *)request; 確定協(xié)議子類是否可以處理指定的請求。
- (void)startLoading; 啟動特定于協(xié)議的請求加載。
- (void)stopLoading; 停止特定于協(xié)議的請求加載。
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request; 返回指定請求的規(guī)范版本。

5.把這四個都實現(xiàn)一下,不要返回值的都可以先空著

// return YES 就是都進行處理抓到就處理
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
    return YES;
}
//返回規(guī)范版本的請求一般直接返回,改變影響查找URL緩存中的對象
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{
    return request;
}
// 啟動特定于協(xié)議的請求加載。
- (void)startLoading{
}
// 停止特定于協(xié)議的請求加載。
-(void)stopLoading{
}

6.運行發(fā)送請求正常順序就是canInitWithRequest-》canonicalRequestForRequest-》startLoading-》stopLoading

但你會發(fā)現(xiàn)你的網(wǎng)絡(luò)請求都會是超時的,接收不到數(shù)據(jù)了
因為你攔截了你發(fā)的網(wǎng)絡(luò)請求然后什么也沒有做,這個請求就相當于沒有發(fā)......所以我們要完善一下startloading方法里的東西 這里我們要做的事就是把攔截的請求發(fā)出去然后返回到外面的請求回調(diào)里

- (void)startLoading{
   //復(fù)制一份獲取攔截的請求
    NSMutableURLRequest *request = [self.request copy];   
    NSURLSessionDataTask *sessionDataTask = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       //將獲取的數(shù)據(jù)回傳給外面的請求
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didFailWithError:error];
        [self.client URLProtocolDidFinishLoading:self];
    }];
    [sessionDataTask resume];
}

7.運行你會發(fā)現(xiàn)一個更大的問題 死循環(huán)了 因為你用的是NSURLSessionDataTask發(fā)的請求 還會被攔截到 攔截到再發(fā) 再攔,所以我們要對我們在startLoading里的請求做一下標識不讓它被攔截 原理就是我們在request對象里人為的添加鍵值進行標識是否被處理了 如果被處理了就在canInitWithRequest方法里返回No不攔截

//定義一個字符串做key
static NSString *xiaDianDealDone = @"xiaDianDealDone";
//修改后的startLoading方法
- (void)startLoading{
    NSMutableURLRequest *request = [self.request copy];
    //為request對象添加一個鍵值標記為YES
    [NSURLProtocol setProperty:@(YES) forKey:xiaDianDealDone inRequest:request];
    
    NSURLSessionDataTask *sessionDataTask = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       //將獲取的數(shù)據(jù)回傳給外面的請求
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didFailWithError:error];
        [self.client URLProtocolDidFinishLoading:self];
    }];
    [sessionDataTask resume];
}
//處理后的canInitWithRequest方法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
   //發(fā)現(xiàn)是處理過的請求直接返回No不攔截此請求
    if ([NSURLProtocol propertyForKey:xiaDianDealDone inRequest:request]) {
        return NO;
    }
    return YES;
}

8.這時候運行就可以在外部獲得網(wǎng)絡(luò)數(shù)據(jù)了 在外面的請求完全看不出來做了什么處理。如果你要對網(wǎng)絡(luò)請求統(tǒng)一做某些處理的時候就在這個protol中就好了

理解

每個網(wǎng)絡(luò)請求被攔截的時候系統(tǒng)都會生成一個protocol子類的對象
這個對象有倆個重要屬性用來處理這個請求

屬性 類型 注釋
request NSURLRequest 攔截的請求的request對象有這個對象能獲取很多request信息
client id <NSURLProtocolClient> 這個是回調(diào)回去重要的屬性,每發(fā)一個網(wǎng)絡(luò)請求系統(tǒng)應(yīng)該都會產(chǎn)生一個client對象來處理網(wǎng)絡(luò)請求進行回調(diào)等操作,而所有的client對象都應(yīng)該遵守的<NSURLProtocolClient>協(xié)議 這樣我們通過回調(diào)協(xié)議的方法就可以把數(shù)據(jù)以及響應(yīng)返回最初的請求

總結(jié)

1.startLoading 里面隨便你用什么再次發(fā)送攔截的網(wǎng)絡(luò)請求 只要能請求就行
2.startLoading里對應(yīng)的回調(diào)方法要回調(diào)對應(yīng)的<NSURLProtocolClient>協(xié)議方法,這樣就能對應(yīng)的在外面獲取到對應(yīng)的響應(yīng)
3.注意死循環(huán)發(fā)送,加上標識。
4.給予蘋果NSURLSession或NSURLConnection的http,https請求可以攔截 如果公司自己實現(xiàn)的應(yīng)用層協(xié)議就不好使了。
5.還有一點多次注冊protocol子類會按照后注冊的線調(diào)用來運行 如果處理的請求就不在像后找,沒處理就接著向后尋找處理protocol

NSURLProtocol很強大 在canInitWithRequest和startLoading里能做的事情就有很多,網(wǎng)絡(luò)請求處理的黑魔法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,931評論 1 92
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,187評論 8 265
  • 沒有您的世界 我學會了很多 我知道,如何去拼命愛 我知道,有多么羨慕別人的感覺 我知道,生命無常 我知道,生與死的...
    小爪紙閱讀 184評論 0 0
  • 你相信每個人一生所經(jīng)歷的挫折是有限的嗎?你身邊是否有那種前半生過得如狗般的辛苦,后半生卻享受著超常人之樂的人。 如...
    愛面包的四夕閱讀 256評論 0 4

友情鏈接更多精彩內(nèi)容