研究筆記:iOS中使用WebViewProxy攔截URL請求
概述
先說明下iOS中載入url的正常流程:
1.客戶端傳送NSURLRequest給server
2.server返回對應的NSURLResponse
如果被WebViewProxy攔截,則流程變為:
1.客戶端傳送NSURLRequest給server
2.這個request被WebViewProxy攔截
3.proxy將修改後的新request傳送給server
4.server返回response給proxy
5.proxy將返回的資料以url response或者回撥的形式返回給客戶端。
那麼WebViewProxy的攔截原理是怎樣的呢?
攔截原理
首先,WebViewProxy定義一個自定義的protocol(NSURLProtocol的子類)
@interface WebViewProxyURLProtocol : NSURLProtocol
題外話,子類必須實現NSURLProtocol的以下幾個方法才能正常工作,當然這部分工作WebViewProxy已經都幫我們搞定了:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client;
- (void)startLoading;
- (void)stopLoading;
然後把URLProtocol的子類註冊到url loading system中
+ (void)initialize {
...
[NSURLProtocol registerClass:[WebViewProxyURLProtocol class]];
}
這樣,以後app每次傳送url request時都檢查此request是否適用於WebViewProxyURLProtocol(過濾條件也是使用者定義的),如果符合篩選條件,則使用WebViewProxyURLProtocol來載入該url。
這裡需要注意下,原始的request和修改後的新request,可能都符合攔截條件,所以為了不使其無限制的攔截並迴圈處理下去,需要設定一個檢測條件,保證每個request至多被處理一次。流程見下圖。
攔截的原理依據如下(NSURLProtocol的registerClass方法):
+ (BOOL)registerClass:(Class)protocolClass
When the URL loading system begins to load a request, each registered protocol class is consulted in turn to see if it can be initialized with the specified request. The first NSURLProtocol subclass to return YES when sent a canInitWithRequest: message is used to perform the URL load. There is no guarantee that all registered protocol classes will be consulted.
Classes are consulted in the reverse order of their registration.
使用舉例
簡單的攔截例子:
[WebViewProxy handleRequestsMatching:[NSPredicate predicateWithFormat:@"host MATCHES[cd] `[foo|bar]`"] handler:^(NSURLRequest* req, WVPResponse *res) {
[res respondWithText:@"Hi!"];
}];
簡單的轉發例子:
[WebViewProxy handleRequestsWithHost:@"example.proxy" handler:^(NSURLRequest *req, WVPResponse *res) {
NSString* proxyUrl = [req.URL.absoluteString stringByReplacingOccurrencesOfString:@"example.proxy" withString:@"example.com"];
NSURLRequest* proxyReq = [NSURLRequest requestWithURL:[NSURL URLWithString:proxyUrl]];
[NSURLConnection connectionWithRequest:proxyReq delegate:res];
}];
更多詳見https://github.com/marcuswestin/WebViewProxy。
值得注意的是處理server返回的response時,有三套api可供選擇:
High level API返回image, text, html or json
Low level API返回HTTP頭和NSData
Piping API:從NSURLConnection返回data/error
注意事項:
註冊自定義protocol可能和其他模組或sdk的攔截功能衝突,導致攔截無效,依據是上文中的:The first NSURLProtocol subclass to return YES when sent a canInitWithRequest: message is used to perform the URL load.
WebViewProxy每次攔截成功後,都會在請求的url尾部加上一個fragment字尾(#__webviewproxyreq__)用來標記該url已攔截,防止下次再次攔截從而造成死迴圈。這樣有個隱患,就是url會被汙染,可能影響某些正常功能。
一個解決方案是在http頭中增加一個標記欄位來表示該url已經被攔截過,從而跳出迴圈。
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
...
// 原有邏輯,註釋掉
//NSString* correctedFragment;
//if (_correctedRequest.URL.fragment) {
// correctedFragment = @"__webviewproxyreq__";
//} else {
// correctedFragment = @"#__webviewproxyreq__";
//}
//_correctedRequest.URL = [NSURL URLWithString:[request.URL.absoluteString stringByAppendingString:correctedFragment]];
//使用http頭來標記的新邏輯 Add by zhouyi.
[_correctedRequest addValue:[@(YES) stringValue] forHTTPHeaderField:webViewProxyFlagKey];
...
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
...
// 新增標記欄位,表示已經被WebViewProxy處理過
// Add by zhouyi.
NSString* proxyFlag = request.allHTTPHeaderFields[webViewProxyFlagKey];
if (proxyFlag)
{
return NO;
}
// 這是原有邏輯,註釋掉
//if ([webViewProxyLoopDetection evaluateWithObject:request.URL])
//{
// return NO;
//}
...
}
相關文章
- iOS 開發中使用 NSURLProtocol 攔截 HTTP 請求iOSProtocolHTTP
- js攔截全域性ajax請求JS
- Android Webview攔截ajax請求AndroidWebView
- fd攔截請求,修改資料
- React、Axios、MockJs實現Ajax的請求攔截ReactiOSMockJS
- vue中用axios攔截器攔截請求和響應VueiOS
- 筆記 - 本地攔截genymotion或者Android模擬器的網路請求筆記Android
- puppeteer去掉同源策略及請求攔截
- iOS請求URL 中文轉譯iOS
- SpringMVC攔截器,設定不攔截的URLSpringMVC
- Springboot通過攔截器攔截請求資訊收集到日誌Spring Boot
- SpringBoot解決跨域請求攔截Spring Boot跨域
- 【轉】AngularJs HTTP請求響應攔截器AngularJSHTTP
- iOS下JS與OC互相呼叫(一)--UIWebView 攔截URLiOSJSUIWebView
- iOS下JS與OC互相呼叫(二)--WKWebView 攔截URLiOSJSWebView
- 從網路請求過程看OkHttp攔截器HTTP
- 使用 Angular HTTP_INTERCEPTOR 攔截器來記錄超時請求的一些思考AngularHTTP
- 自定義攔截器,攔截到了某個請求就返回給前端一個JSON串前端JSON
- Swift學習筆記(3)iOS 9 中的網路請求Swift筆記iOS
- axios攔截器iOS
- axios 攔截器iOS
- IOS 手勢攔截iOS
- 實戰SpringCloud通用請求欄位攔截處理SpringGCCloud
- 有沒有什麼網路請求攔截的庫?
- SpringBoot 攔截器獲取http請求引數Spring BootHTTP
- axios 攔截器 的使用方法iOS
- 前端快閃四: 攔截axios請求和響應前端iOS
- vue+axois 封裝請求+攔截器(請求鎖+統一處理錯誤碼)Vue封裝
- 不能顯式攔截ajax請求的302響應?
- 二次封裝axios,根據引數來實現多個請求多次攔截封裝iOS
- spring mvc中獲取請求URLSpringMVC
- 【axios】XHR的ajax封裝+axios攔截器呼叫+請求取消iOS封裝
- 學習axios必知必會(2)~axios基本使用、使用axios前必知細節、axios和例項物件區別、攔截器、取消請求iOS物件
- 基於 HTTP 請求攔截,快速解決跨域和代理 MockHTTP跨域Mock
- 前端架構之vue+axios 前端實現登入攔截(路由攔截、http攔截)前端架構VueiOS路由HTTP
- Spring Boot中攔截器的使用Spring Boot
- UIWebView攔截圖片請求,SDWebImage下載快取到本地,然後從本地讀取到UIWebView中UIWebView快取
- axios原始碼分析——攔截器iOS原始碼