iOS H5容器的一些探究(二):iOS下的黑魔法NSURLProto

starkbl發表於2021-09-09

一、前言

NSURLProtocol是iOS中的一部分。如果開發者自定義的一個NSURLProtocol並且註冊到app中,那麼在這個自定義的NSURLProtocol中我們可以攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網路請求,然後做到自定義的response返回。非常強大。

二、NSURLProtocol的使用流程

2.1、在AppDelegate中註冊自定義的NSURLProtocol。

比如我這邊自定義的NSURLProtocol叫做YXNSURLProtocol。

@interface YXNSURLProtocol : NSURLProtocol
@end

在系統載入的時候,把自定義的YXNSURLProtocol註冊到URL載入系統中,這樣 所有的URL請求都有機會進入我們自定義的YXNSURLProtocol進行攔截處理。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[YXNSURLProtocol class]];
}

載入完成後,當產生URL請求的同時,會依次進入NSURLProtocol的以下相關方法進行處理,下面我們依次來講一下每一個方法的作用。

2.2、NSURLProtocol中的幾個方法

2.2.1、是否進入自定義的NSURLProtocol載入器

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
    BOOL intercept = YES;
    NSLog(@"YXNSURLProtocol==%@",request.URL.absoluteString);
    if (intercept) {
        
    }
    return intercept;
}

如果返回YES則進入該自定義載入器進行處理,如果返回NO則不進入該自定義選擇器,使用系統預設行為進行處理。
如果這一步驟返回YES。則會進入2.3的方法中。

2.2.2、重新設定NSURLRequest的資訊

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

在這個方法中,我們可以重新設定或者修改request的資訊。比如請求重定向或者新增頭部資訊等等。如果沒有特殊需求,直接返回request就可以了。但是因為這個方法在會在一次請求中被呼叫多次(暫時我也不知道什麼原因為什麼需要回撥多洗),所以request重定向和新增頭部資訊也可以在開始載入中startLoading方法中重新設定。

2.2.3、這個方法主要是用來判斷兩個request是否相同,如果相同的話可以使用快取資料,通常呼叫父類的實現即可

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{
    return [super requestIsCacheEquivalent:a toRequest:b];
}

這個方法基本不常用。

2.2.4、被攔截的請求開始執行的地方

 (void)startLoading{
}

這個函式使我們重點使用的函式。

2.2.5、結束載入URL請求

 (void)stopLoading{
}

2.3、NSURLProtocolClient中的幾個方法

上面的NSURLProtocol定義了一系列載入的流程。而在每一個流程中,我們作為使用者該如何使用URL載入系統,則是NSURLProtocolClient中幾個方法該做的事情。

@protocol NSURLProtocolClient <NSObject>

//請求重定向
- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;

// 響應快取是否合法
- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;

//剛接收到Response資訊
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

//資料載入成功
- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

//資料完成載入
- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

//資料載入失敗
- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;

//為指定的請求啟動驗證
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

//為指定的請求取消驗證
- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

@end

三、實現一個地址重定向的Demo

這個Demo實現的功能是在UIWebView中所有跳轉到sina首頁的請求,都重定位到sohu首頁。

3.1、第一步,新建一個UIWebView,載入sina首頁

_webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
_webView.delegate = self;
[self.view addSubview:_webView];
NSURL *url = [[NSURL alloc] initWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webView loadRequest:request];

3.2、自定義一個NSURLProtocol

@interface YXNSURLProtocolTwo : NSURLProtocol

@end

3.3、在AppDelegate中,進行註冊

 (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[YXNSURLProtocolTwo class]];
    return YES;
}

3.4、在canInitWithRequest方法中攔截/

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
    NSLog(@"canInitWithRequest url-->%@",request.URL.absoluteString);
    
    //看看是否已經處理過了,防止無限迴圈
    if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
        return NO;
    }
    
    NSString *urlString = request.URL.absoluteString;
    if([urlString isEqualToString:@"/"]){
        return YES;
    }
    
    return NO;
}

3.5、在startLoading中進行方法重定向

 (void)startLoading{
    
    NSMutableURLRequest * request = [self.request mutableCopy];
    
    // 標記當前傳入的Request已經被攔截處理過,
    //防止在最開始又繼續攔截處理
    [NSURLProtocol setProperty:@(YES) forKey:URLProtocolHandledKey inRequest:request];
    
    self.connection = [NSURLConnection connectionWithRequest:[self changeSinaToSohu:request] delegate:self];
}

//把所用url中包括sina的url重定向到sohu
- (NSMutableURLRequest *)changeSinaToSohu:(NSMutableURLRequest *)request{
    NSString *urlString = request.URL.absoluteString;
    if ([urlString isEqualToString:@"/"]) {
        urlString = @"";
        request.URL = [NSURL URLWithString:urlString];
    }
    return request;
}

你也可以選擇在+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request替換request。效果都是一樣的。

3.6、因為新建了一個NSURLConnection *connection,所以要實現他的代理方法,如下

 (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

- (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];
}

透過以上幾步,我們就可以實現最簡單的url重定向,WebView載入新浪首頁,卻跳轉到了搜狐首頁。

四、小結

透過自定義的NSURLProtocol,我們拿到使用者請求的request之後,我們可以做很多事情。比如:
1、自定義請求和響應
2、網路的快取處理(H5離線包 和 網路圖片快取)
3、重定向網路請求
4、為測試提供資料Mocking功能,在沒有網路的情況下使用本地資料返回。
5、過濾掉一些非法請求
6、快速進行測試環境的切換
7、攔截圖片載入請求,轉為從本地檔案載入
8、可以攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網路請求。目前WKWebView無法被NSURLProtocol攔截。
9、當有多個自定義NSURLProtocol註冊到系統中的話,會按照他們註冊的反向順序依次呼叫URL載入流程。當其中有一個NSURLProtocol攔截到請求的話,後續的NSURLProtocol就無法攔截到該請求。

五、聯絡方式

如果覺得對你還有些用,就關注小編+喜歡這一篇文章。你的支援是我繼續的動力。

下篇文章預告:iOS 面向切面程式設計的實現與實戰案例

文章來源於網路,如有侵權,請聯絡小編刪除。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3705/viewspace-2818660/,如需轉載,請註明出處,否則將追究法律責任。

相關文章