研究筆記: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
- fd攔截請求,修改資料
- puppeteer去掉同源策略及請求攔截
- SpringBoot解決跨域請求攔截Spring Boot跨域
- SpringMVC攔截器,設定不攔截的URLSpringMVC
- Springboot通過攔截器攔截請求資訊收集到日誌Spring Boot
- React、Axios、MockJs實現Ajax的請求攔截ReactiOSMockJS
- SpringBoot 攔截器獲取http請求引數Spring BootHTTP
- 使用 Angular HTTP_INTERCEPTOR 攔截器來記錄超時請求的一些思考AngularHTTP
- 自定義攔截器,攔截到了某個請求就返回給前端一個JSON串前端JSON
- 不能顯式攔截ajax請求的302響應?
- 實戰SpringCloud通用請求欄位攔截處理SpringGCCloud
- 從網路請求過程看OkHttp攔截器HTTP
- Angular專案簡單使用攔截器 httpClient 請求響應處理AngularHTTPclient
- FastAPI中請求URL傳參ASTAPI
- 有沒有什麼網路請求攔截的庫?
- vue+axois 封裝請求+攔截器(請求鎖+統一處理錯誤碼)Vue封裝
- spring mvc中獲取請求URLSpringMVC
- Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高階配置”SpringMVC
- Spring Boot中攔截器的使用Spring Boot
- 基於 HTTP 請求攔截,快速解決跨域和代理 MockHTTP跨域Mock
- iOS 使用Moya網路請求iOS
- URL請求不能解決中文請求的問題
- 使用 useRequestURL 組合函式訪問請求URL函式
- OkHttp 原始碼剖析系列(一)——請求的發起及攔截器機制概述HTTP原始碼
- SpringMVC中的攔截器SpringMVC
- grpc中的攔截器RPC
- JavaScript 中如何攔截全域性 Fetch API 的請求和響應?JavaScriptAPI
- Spring MVC 基於URL的攔截和對映規則SpringMVC
- SpringBoot攔截器中獲取註解、攔截器中注入ServiceSpring Boot
- spring mvc 攔截器的使用SpringMVC
- 攔截器的使用問題
- 在springboot中使用攔截器Spring Boot
- 請求轉發與URL重定向
- get,post URL加字尾請求
- Swoft 學習筆記之 request 請求筆記
- iOS 使用form表單形式提交post請求iOSORM
- 視訊直播系統原始碼,使用者選擇免打擾模式時的請求響應攔截原始碼模式
- Asp.Netcore使用Filter來實現介面的全域性異常攔截,以及前置攔截和後置攔截ASP.NETNetCoreFilter