博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
研究笔记:iOS中使用WebViewProxy拦截URL请求
阅读量:5768 次
发布时间:2019-06-18

本文共 3833 字,大约阅读时间需要 12 分钟。

概述

先说明下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
)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];}];

更多详见。

值得注意的是处理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
)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; //} ...}

转载地址:http://xddux.baihongyu.com/

你可能感兴趣的文章
卡特兰数
查看>>
006_mac osx 应用跨屏幕
查看>>
nginx中配置文件的讲解
查看>>
MindNode使用
查看>>
SQL Server 2016 Alwayson新增功能
查看>>
HTTP库Axios
查看>>
CentOS7下安装python-pip
查看>>
认知计算 Cognitive Computing
查看>>
左手坐标系和右手坐标系 ZZ
查看>>
陀螺仪主要性能指标
查看>>
Java 架构师眼中的 HTTP 协议
查看>>
Linux 目录结构和常用命令
查看>>
Linux内存管理之mmap详解 (可用于android底层内存调试)
查看>>
利润表(年末)未分配利润公式备份
查看>>
Android开发中ViewStub的应用方法
查看>>
gen already exists but is not a source folder. Convert to a source folder or rename it 的解决办法...
查看>>
HDOJ-2069Coin Change(母函数加强)
查看>>
遍历Map的四种方法
查看>>
JAVA学习:maven开发环境快速搭建
查看>>
Altium Designer 小记
查看>>