WKWebView实现请求拦截,http/https/file等

7,680 阅读2分钟

本文是通过WKURLSchemeHandler的方式实现的请求拦截,WKWebViewWKURLSchemeHandler是不支持拦截http/https/file等原始协议的,如果通过[config setURLSchemeHandler:handler forURLScheme:@"http"];就会闪退,但是支持自定义协议,但是有时候项目就是需要拦截这些请求,该怎么处理呢?

一、自定义一个HFURLSchemeHandler类,并实现WKURLSchemeHandler代理

1、HFURLSchemeHandler.h文件实现,遵循<WKURLSchemeHandler>代理

#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>

@interface HFURLSchemeHandler : NSObject <WKURLSchemeHandler>

@end

2、HFURLSchemeHandler.m文件实现两个代理方法- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask

#import "HFURLSchemeHandler.h"

@implementation HFURLSchemeHandler

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask{
    NSURLRequest *request = urlSchemeTask.request;
    NSString *urlStr = request.URL.absoluteString;
    
    
    if ([urlStr hasPrefix:@"file://"]) {
        // scheme切换回本地文件协议file://
        NSURLRequest *fileUrlRequest = [[NSURLRequest alloc] initWithURL:request.URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:0];
        // 异步加载本地资源
        NSURLSession *session = [NSURLSession sharedSession];
        // 请求加载任务
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:fileUrlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                [urlSchemeTask didFailWithError:error];
            } else {
                NSDictionary *headerFields = @{
                    @"Content-Type":[NSString stringNullDealWith:response.MIMEType],
                    @"Content-Length":[NSString stringWithFormat:@"%ld", data.length]
                };
                response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];
                
                // 将数据回传给webView
                [urlSchemeTask didReceiveResponse:response];
                [urlSchemeTask didReceiveData:data];
                [urlSchemeTask didFinish];
            }
        }];
        [dataTask resume];
    }
}

- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
    NSLog(@"stop = %@",urlSchemeTask);
    [urlSchemeTask didFinish];
}

二、在WKWebView中配置需要拦截的协议名称

如果setURLSchemeHandler: forURLScheme:中的URLScheme设置的是http/https/file,会出现崩溃,但是可设置自定义的URLScheme,例如:@"Customer";如果要实现对http/https/file的拦截,请查看第三步。

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 配置请求拦截(根据协议拦截)
HFURLSchemeHandler *handler = [HFURLSchemeHandler new];
[config setURLSchemeHandler:handler forURLScheme:@"file"];

三、配置允许拦截http/https/file

实现这个需求,则需要借助runtime的知识,方法替换method_exchangeImplementations,将WKWebView中的handlesURLScheme方法替换成自定义的方法。

给WKWebView增加一个分类,取名为WKWebView+SchemeHandle

1、WKWebView+SchemeHandle.h

#import <WebKit/WebKit.h>

@interface WKWebView (SchemeHandle)

@end

2、WKWebView+SchemeHandle.m 重写+ (void)load方法进行方法替换

#import "WKWebView+SchemeHandle.h"
#import <objc/runtime.h>

@implementation WKWebView (SchemeHandle)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalMethod1 = class_getClassMethod(self, @selector(handlesURLScheme:));
        Method swizzledMethod1 = class_getClassMethod(self, @selector(yyhandlesURLScheme:));
        method_exchangeImplementations(originalMethod1, swizzledMethod1);
    });
}

+ (BOOL)yyhandlesURLScheme:(NSString *)urlScheme {
    if ([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"] || [urlScheme isEqualToString:@"file"]) {
        return NO//这里让返回NO,应该是默认不走系统断言或者其他判断啥的
    } else {
        return [self yyhandlesURLScheme:urlScheme];
    }
}
@end

至此,就可以实现WKWebView的请求拦截了。