18

我如何监控 WKWebview 上的请求?

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

4

4 回答 4

34

最后我解决了

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

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

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];
}
于 2015-03-03T09:40:44.903 回答
17

@Benzi Heler 的答案很棒,但它使用了 jQuery,这似乎不再适用WKWebView了,所以我找到了不使用 jQuery 的解决方案。

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

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 写的这个答案的启发: https ://stackoverflow.com/a/27363569/3448282

于 2018-09-21T07:03:44.150 回答
6

如果您可以控制其中的内容,则WkWebView可以在发出 ajax 请求时使用向您的本机应用程序发送消息window.webkit.messageHandlers,该请求将作为WKScriptMessage可以被您指定为WKScriptMessageHandler. 消息可以包含您想要的任何信息,并且会在您的 Objective-C 或 Swift 代码中自动转换为本机对象/值。

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

于 2015-03-02T06:25:38.400 回答
2

您可以使用它来响应来自 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
}
于 2020-09-21T20:05:58.683 回答