我如何监控 WKWebview 上的请求?

How can i monitor requests on WKWebview?

如何监控 WKWebview 上的请求?

我试过使用 NSURLprotocol (canInitWithRequest) 但它不会监控 ajax 请求 (XHR),只能监控导航请求(文档请求)

如果您可以控制 WkWebView 中的内容,您可以在每次发出 ajax 请求时使用 window.webkit.messageHandlers 向您的本机应用程序发送消息,该请求将作为WKScriptMessage that can be processed by whatever you've designated as your WKScriptMessageHandler。这些消息可以包含您想要的任何信息,并且会在您的 Objective-C 或 Swift 代码中自动转换为本机 objects/values。

如果您无法控制内容,您仍然可以通过 WKUserScript 注入自己的 JavaScript 来跟踪 ajax 请求并使用上述方法。

我终于解决了

由于我无法控制 Web 视图内容,我向 WKWebview 注入了一个 java 脚本,其中包含一个 jQuery AJAX 请求侦听器。

当侦听器捕获请求时,它会在方法中向本机应用程序发送请求正文:

webkit.messageHandlers.callbackHandler.postMessage(data);

本机应用程序在委托中捕获消息:

(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message

并执行相应的操作

相关代码如下:

ajaxHandler.js -

//Every time an Ajax call is being invoked the listener will recognize it and  will call the native app with the request details

$( document ).ajaxSend(function( event, request, settings )  {
    callNativeApp (settings.data);
});

function callNativeApp (data) {
    try {
        webkit.messageHandlers.callbackHandler.postMessage(data);
    }
    catch(err) {
        console.log('The native context does not exist yet');
    }
}

我的 ViewController 代表是:

@interface BrowserViewController : UIViewController <UIWebViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIWebViewDelegate>

在我的 viewDidLoad() 中,我正在创建一个 WKWebView:

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
[self addUserScriptToUserContentController:configuration.userContentController];
appWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:configuration];
appWebView.UIDelegate = self;
appWebView.navigationDelegate = self;
[appWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: @"http://#############"]]];                                                     

这是 addUserScriptToUserContentController:

- (void) addUserScriptToUserContentController:(WKUserContentController *) userContentController{
    NSString *jsHandler = [NSString stringWithContentsOfURL:[[NSBundle mainBundle]URLForResource:@"ajaxHandler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:NULL];
    WKUserScript *ajaxHandler = [[WKUserScript alloc]initWithSource:jsHandler injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];
    [userContentController addScriptMessageHandler:self name:@"callbackHandler"];
    [userContentController addUserScript:ajaxHandler];
}

@Benzi Heler 回答很好,但它使用 jQuery 似乎不再适用于 WKWebView,所以我找到了不使用 jQuery.[=22 的解决方案=]

这是 ViewController 实现,它让您在 WKWebView 中完成每个 AJAX 请求时收到通知:

import UIKit
import WebKit

class WebViewController: UIViewController {

    private var wkWebView: WKWebView!
    private let handler = "handler"

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()
        let userScript = WKUserScript(source: getScript(), injectionTime: .atDocumentStart, forMainFrameOnly: false)
        config.userContentController.addUserScript(userScript)
        config.userContentController.add(self, name: handler)

        wkWebView = WKWebView(frame:  view.bounds, configuration: config)
        view.addSubview(wkWebView)

        if let url = URL(string: "YOUR AJAX WEBSITE") {
            wkWebView.load(URLRequest(url: url))
        } else {
            print("Wrong URL!")
        }
    }

    private func getScript() -> String {
        if let filepath = Bundle.main.path(forResource: "script", ofType: "js") {
            do {
                return try String(contentsOfFile: filepath)
            } catch {
                print(error)
            }
        } else {
            print("script.js not found!")
        }
        return ""
    }
}

extension WebViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if let dict = message.body as? Dictionary<String, AnyObject>, let status = dict["status"] as? Int, let responseUrl = dict["responseURL"] as? String {
            print(status)
            print(responseUrl)
        }
    }
}

相当标准的实施。有一个 WKWebView 以编程方式创建。有从 script.js 文件加载的注入脚本。

最重要的部分是script.js文件:

var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
    this.addEventListener("load", function() {
        var message = {"status" : this.status, "responseURL" : this.responseURL}
        webkit.messageHandlers.handler.postMessage(message);
    });
    open.apply(this, arguments);
};

userContentController 委托方法将在每次加载 AJAX 请求时被调用。我路过 statusresponseURL,因为这正是我所需要的,但您也可以获得有关请求的更多信息。以下是所有可用属性和方法的列表: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

我的解决方案受到@John Culviner 撰写的这个答案的启发:

您可以使用它来响应来自 WKWebView 的请求。它的工作方式类似于 UIWebView。

- (void)webView:(WKWebView *)webView2 decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
     
        NSString *url = [navigationAction.request.URL absoluteString];
        
       // Handle URL request internally

    }

    decisionHandler(WKNavigationActionPolicyAllow); // Will continue processing request

    decisionHandler(WKNavigationActionPolicyCancel); // Cancels request
}