本章介绍UIWebView和WKWebView的网络请求拦截和请求更改-- 案例demo
注意UIWebView已经被抛弃替换为WKWebView,可以作为参考,将其翻译成WKWebView的相关逻辑
UIWebView网络请求拦截、篡改
UIWebView的拦截是通过重写NSURLProtocol类,来实现我们自定义的拦截,然后在使用WebView的时候注册URLProtocol即可
可以通过继承重写NSURLProtocol类需要重写下面几个方法:
canInitWithRequest、canonicalRequestForRequest、requestIsCacheEquivalent、startLoading、stopLoading
canInitWithRequest
该方法是判断是否拦截处理对应请求的方法,可以通过返回YES,来阻断改请求,当请求阻断后会走后面的方法
拦截方式如下所示,可以通过url、后缀名之类的屏蔽
//是否能够处理给定的请求,自行出行返回YES,否则返回NO
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
NSLog(@"%@", request.URL.absoluteString);
//拦截百度的logo
NSArray *blackList = @[@"jpeg", @"png", @"jpg"];
if ([request.URL.absoluteString
isEqualToString:@"https://www.baidu.com/img/flexible/logo/plus_logo_web_2.png"]) {
//屏蔽单个url
return YES;
}else if ([blackList containsObject:request.URL.pathExtension]) {
//拦截掉一类数据例如图片
return YES;
// else if ([request.URL.absoluteString containsString:@"www.baidu.com"]) {
// //是否包含某个url,或者其他,可以屏蔽一类网站
// return YES;
// }
return NO;
canonicalRequestForRequest、requestIsCacheEquivalent
canonicalRequestForRequest为拦截请求后必须实现的方法,requestIsCacheEquivalent无需实现也行,一般不在这里做额外的操作
//必须实现,规范化URL请求,一般返回request
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
//用于检测两个请求是缓存等效的,则为YES,否则为NO
//+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
// return [super requestIsCacheEquivalent:a toRequest:b];
startLoading
startLoading是拦截完毕请求后,进行篡改内容的重要一步,可以在里面根据拦截的请求,来替换篡改请求数据
通过NSURLProtocolClient的-URLProtocol:didLoadData方法来发送替换数据
如下所示,将看到的图片替换成自己想要的图片内容
- (void)startLoading {
//拦截后加载请求,可以在这里调整发起自己的网络请求
NSArray *blackList = @[@"jpeg", @"png", @"jpg"];
if ([self.request.URL.absoluteString isEqualToString:
@"https://www.baidu.com/img/flexible/logo/plus_logo_web_2.png"]) {
NSData *imgData = [NSData dataWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:@"test1" ofType:@"jpeg"]];
[self.client URLProtocol:self didLoadData:imgData];
}else if ([blackList containsObject:self.request.URL.pathExtension]) {
//将拦截的图片替换掉
NSData *imgData = [NSData dataWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:@"test1" ofType:@"jpeg"]];
[self.client URLProtocol:self didLoadData:imgData];
// else if ([self.request.URL.absoluteString containsString:@"www.baidu.com"]) {
// //是否包含某个url,或者其他,可以屏蔽一类网站
// }
对重写的NSURLProtocol子类进行注册和移除
注意:仅仅重写是不够的的,还需要在使用前注册该重写后的子类,并且使用完毕该功能后,适当的位置及时移除协议(可以在使用该webView前注册,离开该页面后取消注册),可以避免其他网络被拦截的情况
//注册拦截协议
[NSURLProtocol registerClass:[LSURLProtocol class]];
//取消注册
[NSURLProtocol unregisterClass:[LSURLProtocol class]];
最后测试一下,发现百度的首页图片已经被拦截替换了
WKWebView的网络请求拦截、篡改
WKWebView和UIWebView不一样,其核心模块是在WebKit框架中,网络也是一样,因此其拦截不能像UIWebView一样了,需要通过WebKit框架中的新方法来实现对指定WKWebView内容的拦截和篡改
将WKWebView拦截之前,先贴出WKWebView的正常加载代码,从中可以看到WKWebViewConfiguration,这个是注册拦截用到的类
- (void)initWKWebView {
//网络走的webkit内核,无法使用NSURLProtocol
//不加上webView显示大小有问题
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;
_wkWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:wkWebConfig];
//手势触摸滑动
_wkWebView.allowsBackForwardNavigationGestures = YES;
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:@"https://www.baidu.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
[_wkWebView loadRequest:request];
[self.view addSubview:_wkWebView];
设置WKURLSchemeHandler代理,以及注意事项
WKWebView实现网络拦截是通过的WKURLSchemeHandler协议,通过WKWebViewConfiguration对象进行注册,由于WKWebViewConfiguration是直接跟WKWebView挂钩的,所以不需要担心其移除问题(当然除非所有的WKWebView使用的都是这一个config)
设置的时候,可以设置拦截的URLScheme,拦截前会调用WKWebView的类方法handlesURLScheme,来检查设置的URLScheme是否符合条件,如果不符合直接崩溃,例如下面所示:http系列的会导致崩溃,正常url不会有问题,详细可以查看handlesURLScheme说明
其设置代理代码如下所示:
//可以点进去查看,通过webView的类方法handlesURLScheme来检查url可用性,无效的URLScheme会导致崩溃(例如:http)
//由于调用类方法继承不可以,可以通过分类+hook来处理(或者主动方法的hook和交换),避免自定义拦截标准出现出现崩溃
//标准的urlscheme是没有问题的,可以点方法进入查看参考标准,例如:www.baidu.com
//这里可以自行创建专门处理拦截业务的代理类,将self替换之,并实现WKURLSchemeHandler协议即可
[wkWebConfig setURLSchemeHandler:self forURLScheme:@"www.baidu.com"];
[wkWebConfig setURLSchemeHandler:self forURLScheme:@"https"];
[wkWebConfig setURLSchemeHandler:self forURLScheme:@"http"];
那么如何避免设置非法的URLScheme崩溃呢,前面说了通过WKWebView的类方法handlesURLScheme,因此可以hook掉该类方法,可以通过以分类或者是直接hook的方式来进行hook(这里为了看着更清晰,在分类里面进行的hook)
hook代码如下所示:
+ (void)load {
method_exchangeImplementations(
class_getClassMethod(self, @selector(handlesURLScheme:)),
class_getClassMethod(self, @selector(__handlesURLScheme:)));
+ (BOOL)__handlesURLScheme:(NSString *)urlScheme {
if ([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"]) {
return NO;
return [self __handlesURLScheme: urlScheme];
这样就不会崩溃了
实现代理拦截、篡改请求
代理主要实现startURLSchemeTask方法,这个方法相当于是URLProtocol中的canInitWithRequest与startLoading的结合体,可以通过startURLSchemeTask方法拦截并篡改数据
案例如下所示
//通知开始准备加载相应的网络任务
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask {
NSURLRequest *request = urlSchemeTask.request;
//可以通过url拦截响应的方法
if ([request.URL.absoluteString.pathExtension isEqualToString:@"png"]
|| [request.URL.absoluteString.pathExtension isEqualToString:@"gif"]) {
//一个任务完成需要返回didReceiveResponse和didReceiveData两个方法
//最后在执行didFinish,不可重复调用,可能会导致崩溃
[urlSchemeTask didReceiveResponse:[NSURLResponse new]];
[urlSchemeTask didReceiveData:[NSData dataWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:@"test1" ofType:@"jpeg"]]];
[urlSchemeTask didFinish];
return;
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//也可以通过解析data等数据,通过data等数据来确定是否拦截
//一个任务完成需要返回didReceiveResponse和didReceiveData两个方法
//最后在执行didFinish,不可重复调用,可能会导致崩溃
if (!data) {
[urlSchemeTask didReceiveResponse:[NSURLResponse new]];
[urlSchemeTask didReceiveData:[NSData dataWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:@"test1" ofType:@"jpeg"]]];
} else {
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
[task resume];
通过上面代码可以看到,当一个请求被拦截时,必须要调用一系列的方法,保证请求能够正常进行,通过urlSchemeTask参数,可以检验URL相关数据,并且拦截后替换返回数据,需要分别调用didReceiveResponse、didReceiveData回调数据,并通过didFinish方法标记成功(必须在回调数据之后标记),且方法不可重复调用,不然可能会导致崩溃
当然通过上面案例也可以看出,也可以先请求网络,然后通过网络返回的结果,来判断是否对数据进行拦截和篡改,可以参考上面的案例
当通知停止加载某个url时会调用下面的stopURLSchemeTask方法,该方法一般不回调,可以用来查看失败原因,并作出相应
//通知停止加载该url,这里一般不做处理,否则可能会引起异常,暂时未发现走这里
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask {
NSLog(@"%@", urlSchemeTask.request.URL.absoluteString);
测试一下,与UIWebView一样,发现百度的首页图片已经被拦截替换了
以上便是webView拦截请求的相关内容了,想要更加详细的内容,可以使用案例demo,点入方法查看系统的说明并尝试