NSURLProtocol之網(wǎng)絡(luò)攔截

Qinz
NSURLProtocol是一個抽象類捧颅,需要子類去實例化,在使用的時候注冊該子類较雕,則可在自定義的 NSURLProtocol 中攔截所有的請求碉哑,進行廣告過濾或重定向等操作,下面將以攔截百度為例分析該過程及使用方法亮蒋。
1. 首先我們需要創(chuàng)建一個NSURLProtocol的子類扣典,在使用的時候進行注冊:
   [NSURLProtocol registerClass:[QURLProtocol class]];
  • 1.1 這里注意要釋放
- (void)dealloc{
    [NSURLProtocol unregisterClass:[KCURLProtocol class]];
}
2. 接下來在子類中重寫必須實現(xiàn)的5個方法:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
- (void)startLoading;
- (void)stopLoading;
3. 我們在控制器中加載一個百度的網(wǎng)頁
  NSURLRequest *request = [NSURLRequest requestWithURL: 
  [NSURL URLWithString:@"https://www.baidu.com/"]];
  [self.webView loadRequest:request];
4. 在canInitWithRequest方法中攔截百度網(wǎng)址,代碼如下:
+(BOOL)canInitWithRequest:(NSURLRequest *)request{
    //已經(jīng)攔截過的就不再k攔截,避免死循環(huán)
    if ([NSURLProtocol propertyForKey:QZProtocolKey inRequest:request]) {
        return NO;
    }
   //攔截百度,這里可以使用isEqualToString進行精準(zhǔn)攔截
    if ([[request.URL absoluteString] containsString:@"www.baidu.com"]) {

        return YES;
    }
    return NO;
}
5. 接下來在startLoading對攔截的地址進行重定向慎玖,代碼如下:
- (void)startLoading{
    
    //標(biāo)記贮尖,下次不攔截自己設(shè)置的
    [NSURLProtocol setProperty:@(YES) forKey:QZProtocolKey inRequest:[self.request mutableCopy]];
 
    //重定向
    if ([[self.request.URL absoluteString] isEqualToString:@"https://www.baidu.com/"]) {
      
        NSString*url = @"http://www.reibang.com/";
        NSURLRequest*myRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
        
        NSURLSessionConfiguration *configuration =
        [NSURLSessionConfiguration defaultSessionConfiguration];
        
        self.queue = [[NSOperationQueue alloc] init];
        self.queue.maxConcurrentOperationCount = 1;
        self.queue.name = @"com.Qinz.cn";
        
        NSURLSession *session =
        [NSURLSession sessionWithConfiguration:configuration
                                      delegate:self
                                 delegateQueue:self.queue];
        //偷梁換柱
        self.task = [session dataTaskWithRequest:myRequest];
        [self.task resume];
        
    }
}
6. 記得在stopLoading方法中對任務(wù)進行取消:
- (void)stopLoading{
    
     [self.task cancel];
}
7. 在NSURLSessionDataDelegate中對重定向的數(shù)據(jù)進行處理:
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data {
    
    if ([self.request.URL.absoluteString isEqualToString:@"https://www.baidu.com/"]) {
        // 將接收到的數(shù)據(jù)返回給系統(tǒng)處理
        [self.client URLProtocol:self didLoadData:data];
    }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
    
    if (response != nil){
        [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
    }
}
8. 這里簡單演示下廣告過濾,因為廣告一般為圖片趁怔,所以我們攔截相關(guān)類型的圖片湿硝,然后替換為自己的數(shù)據(jù)薪前,代碼如下:
+(BOOL)canInitWithRequest:(NSURLRequest *)request{
    
    //Hook圖片,用于廣告過濾等
    NSArray *array = @[@"png", @"jpeg", @"gif", @"jpg"];
    if([array containsObject:request.URL.pathExtension]){
        return YES;
    }
    return NO;
}

- (void)startLoading{
    //過濾廣告
    NSArray *array = @[@"png",@"jpg",@"jpeg"];
    if ([array containsObject:[self.request.URL pathExtension]]) {
        NSData *data = [self getImageData];
        [self.client URLProtocol:self didLoadData:data];
    }
    
}
  • 8.1 百度的logo已經(jīng)被替換关斜,當(dāng)然這里只是簡單演示給出思路示括,具體思想還有很多細(xì)節(jié)要處理。


    image.png
9. 上面還有兩個方法痢畜,沒特殊需求重寫父類即可:
//返回規(guī)范的request
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{   
    return request;
}
/**
這個方法主要用來判斷兩個請求是否是同一個請求垛膝,如果是,則可以使用緩存數(shù)據(jù)丁稀,通常只需要調(diào)用父類的實現(xiàn)即可,默認(rèn)為YES
 */
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
}
10. 當(dāng)我們對Session進行攔截時會發(fā)現(xiàn)不成功(如AF)吼拥,這里需要進行特殊處理:
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    NSString *url  = @"http://www.baidu.com";
    [manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        NSLog(@"AFN---%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"AFN---%@",error);
    }];
11. 交換系統(tǒng)Session配置方法,返回我們自己的子類即可:
#pragma mark - hook

+ (void)hookNSURLSessionConfiguration{
    
    Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
    
    Method originalMethod = class_getInstanceMethod(cls, @selector(protocolClasses));
    Method stubMethod = class_getInstanceMethod([self class], @selector(protocolClasses));
    if (!originalMethod || !stubMethod) {
        [NSException raise:NSInternalInconsistencyException format:@"沒有這個方法 無法交換"];
    }
    method_exchangeImplementations(originalMethod, stubMethod);
}

- (NSArray *)protocolClasses {
    return @[[QURLProtocol class]];
    //如果還有其他的監(jiān)控protocol,也可以在這里加進去
}
注意:當(dāng)我們在startLoading進行攔截處理時线衫,要做好對應(yīng)的邏輯判斷凿可,否則會引發(fā)崩潰!

以上就是對NSURLProtocol攔截網(wǎng)絡(luò)詳細(xì)分析授账,當(dāng)然NSURLProtocol還可以做很多事情矿酵,如增加公共請求頭,對API進行一些訪問的統(tǒng)計等矗积。最后附上Demo下載全肮,如果幫助到你請給一個Star!

我是Qinz,希望我的文章對你有幫助棘捣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辜腺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乍恐,更是在濱河造成了極大的恐慌评疗,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茵烈,死亡現(xiàn)場離奇詭異百匆,居然都是意外死亡,警方通過查閱死者的電腦和手機呜投,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門加匈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人仑荐,你說我怎么就攤上這事雕拼。” “怎么了粘招?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵啥寇,是天一觀的道長。 經(jīng)常有香客問我,道長辑甜,這世上最難降的妖魔是什么衰絮? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮磷醋,結(jié)果婚禮上猫牡,老公的妹妹穿的比我還像新娘。我一直安慰自己子檀,他們只是感情好镊掖,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布乃戈。 她就那樣靜靜地躺著褂痰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪症虑。 梳的紋絲不亂的頭發(fā)上缩歪,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音谍憔,去河邊找鬼匪蝙。 笑死,一個胖子當(dāng)著我的面吹牛习贫,可吹牛的內(nèi)容都是我干的逛球。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼苫昌,長吁一口氣:“原來是場噩夢啊……” “哼颤绕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起祟身,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤奥务,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后袜硫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氯葬,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年婉陷,在試婚紗的時候發(fā)現(xiàn)自己被綠了帚称。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡秽澳,死狀恐怖世杀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肝集,我是刑警寧澤瞻坝,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響所刀,放射性物質(zhì)發(fā)生泄漏衙荐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一浮创、第九天 我趴在偏房一處隱蔽的房頂上張望忧吟。 院中可真熱鬧,春花似錦斩披、人聲如沸溜族。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煌抒。三九已至,卻和暖如春厕倍,著一層夾襖步出監(jiān)牢的瞬間寡壮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工讹弯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留况既,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓组民,卻偏偏與公主長得像棒仍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子臭胜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344