iOS URL Loading System / HTTP 重定向 認識與學習
一個朋友問了我一個問題,需求是這樣的:他要用本地的H5資源 替換 鏈接資源, 但是判斷鏈接資源時候 因為一些操作請求本地化了之后 一些操作比如請求服務器使用的是http開頭,然而本地資源一直是以file://開頭, 這樣的
(1)如果需要,可以對html頁面中的圖片做本地化處理
(2)Mock假的response
(3)對請求頭做規范化處理
(4)在上層應用不感知情況下,實現一套代理機制
(5)過濾請求、響應中敏感信息
(6)對已有協議做改進、補充處理
這些是網上查得到的,總得來說就是攔截請求時候 可以高度自定義請求方式, 攔截請求結果 高度自定義處理方法. 在實際開發中,根據具體需求處理.
我寫了一個 實例 參考SectionDemo 的CustomUrlProtocol
// // MyURLProtocol.h // NSURLProtocolExample // // Created by HF on 2017/5/3. // Copyright ? 2017年 HF. All rights reserved. // #import <Foundation/Foundation.h> @interface MyURLProtocol : NSURLProtocol @property (nonatomic, strong) NSMutableData *mutableData; @property (nonatomic, strong) NSURLResponse *response; @end
// // MyURLProtocol.m // NSURLProtocolExample // // Created by HF on 2017/5/3. // Copyright ? 2017年 HF. All rights reserved. // #import "MyURLProtocol.h" #import "AppDelegate.h" #import "CachedURLResponseModel+CoreDataProperties.h" @interface MyURLProtocol () <NSURLConnectionDelegate> @property (nonatomic, strong) NSURLConnection *connection; @end @implementation MyURLProtocol /** * 是否攔截處理指定的請求 * * @param request 指定的請求 * * @return 返回YES表示要攔截處理,返回NO表示不攔截處理 */ + (BOOL)canInitWithRequest:(NSURLRequest *)request { static NSUInteger requestCount = 0; NSLog(@"Request #%lu: URL = %@", (unsigned long)requestCount++, request); //看看是否已經處理過了,防止無限循環 if ([NSURLProtocol propertyForKey:@"MyURLProtocolHandledKey" inRequest:request]) { return NO; } return YES; } #pragma mark - NSURLProtocol /** 重寫這個協議 目的是按需求條件篩選出目標請求,同時對目標request進行進一步完整包裝與定義 @param request request @return NSURLRequest */ + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { NSMutableURLRequest *mutableReqeust = [request mutableCopy]; mutableReqeust = [self redirectHostInRequset:mutableReqeust]; return mutableReqeust; //return request; } + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b { return [super requestIsCacheEquivalent:a toRequest:b]; } - (void)startLoading { //如果想直接返回緩存的結果,構建一個CachedURLResponse對象 // 1. CachedURLResponseModel *cachedResponse = [self cachedResponseForCurrentRequest]; if (cachedResponse) { NSLog(@"serving response from cache"); // 2. NSData *data = cachedResponse.data; NSString *mimeType = cachedResponse.mimeType; NSString *encoding = cachedResponse.encoding; // 3. NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:mimeType expectedContentLength:data.length textEncodingName:encoding]; // 4. [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [self.client URLProtocol:self didLoadData:data]; [self.client URLProtocolDidFinishLoading:self]; } else { // 5. NSLog(@"serving response from NSURLConnection"); NSMutableURLRequest *newRequest = [self.request mutableCopy]; //標記"tag",防止無限循環 [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest]; self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self]; } } - (void)stopLoading { [self.connection cancel]; self.connection = nil; } #pragma mark - NSURLConnectionDelegate - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; self.response = response; self.mutableData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.client URLProtocol:self didLoadData:data]; [self.mutableData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [self.client URLProtocolDidFinishLoading:self]; [self saveCachedResponse]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { [self.client URLProtocol:self didFailWithError:error]; } #pragma mark -- private +(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request { if ([request.URL host].length == 0) { return request; } NSString *originUrlString = [request.URL absoluteString]; NSString *originHostString = [request.URL host]; NSRange hostRange = [originUrlString rangeOfString:originHostString]; if (hostRange.location == NSNotFound) { return request; } //定向到bing搜索主頁 NSString *ip = @"cn.bing.com"; // 替換host NSString *urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:ip]; NSURL *url = [NSURL URLWithString:urlString]; request.URL = url; return request; } - (void)saveCachedResponse { NSLog(@"saving cached response"); // if (![self.request.URL.absoluteString isEqualToString:@"cn.bing.com"]) return; // 1. AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate]; NSManagedObjectContext *context = delegate.managedObjectContext; // 2. CachedURLResponseModel *cachedResponse = [NSEntityDescription insertNewObjectForEntityForName:@"CachedURLResponseModel"inManagedObjectContext:context]; cachedResponse.data = self.mutableData; cachedResponse.url = self.request.URL.absoluteString; cachedResponse.timestamp = [NSDate date]; cachedResponse.mimeType = self.response.MIMEType; cachedResponse.encoding = self.response.textEncodingName; // 3. NSError *error; BOOL const success = [context save:&error]; if (!success) { NSLog(@"Could not cache the response."); } } - (CachedURLResponseModel *)cachedResponseForCurrentRequest { // 1. AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate]; NSManagedObjectContext *context = delegate.managedObjectContext; // 2. NSFetchRequest *fetchRequest = [CachedURLResponseModel fetchRequest]; // 3. NSPredicate *predicate = [NSPredicate predicateWithFormat:@"url == %@", self.request.URL.absoluteString]; [fetchRequest setPredicate:predicate]; // 4. NSError *error; NSArray *result = [context executeFetchRequest:fetchRequest error:&error]; // 5. if (result && result.count > 0) { return result[0]; } return nil; } @end
然后在需要的請求前注冊這個類
[NSURLProtocol registerClass:[MyURLProtocol class]];
請求結束 注銷這個類
[NSURLProtocol unregisterClass:[MyURLProtocol class]];
在MyURLProtocol 里面 針對目標請求 具體按需處理
這里 我舉得例子 是:
首先 :請求 "https://www.raywenderlich.com" 使用 "MyURLProtocol" 注冊 攔截 該請求 然后重定向到 "cn.bing.com"上
其次:對重定向 對象 添加緩存
注意:
這里只是模擬過程 沒有特此針對判斷具體鏈接,真正使用的時候大家一定要邏輯嚴謹,并且根據具體需求要做適當優化,才能靈活達到舉一反三目的。
參考:
posted on 2017-05-02 10:48 ACM_Someone like you 閱讀(1778) 評論(0) 收藏 舉報
浙公網安備 33010602011771號