javascript 如何监控 WKWebview 上的请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28766676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 09:31:37  来源:igfitidea点击:

How can i monitor requests on WKWebview?

javascriptiosmonitorwkwebviewnsurlprotocol

提问by Benzi Heler

How can i monitor requests on WKWebview?

如何监控 WKWebview 上的请求?

I'v tried using NSURLprotocol (canInitWithRequest) but it won't monitor ajax requests (XHR), only navigation requests(document requests)

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

回答by Benzi Heler

Finally I solved it

最后我解决了

Since I don't have control over the web view content, I injected to the WKWebview a java script that include a jQuery AJAX request listener.

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

When the listener catches a request it sends the native app the request body in the method:

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

webkit.messageHandlers.callbackHandler.postMessage(data);

The native app catches the message in a delegate called:

本机应用程序在一个名为的委托中捕获消息:

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

and perform the corresponding actions

并执行相应的动作

here is the relevant code:

这是相关的代码:

ajaxHandler.js -

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');
    }
}

My ViewController delegate are:

我的 ViewController 委托是:

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

And in my viewDidLoad(), I'm creating a WKWebView:

在我的 中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://#############"]]];                                                     

Here is the addUserScriptToUserContentController:

这是 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];
}

回答by user3448282

@Benzi Heler answer is great, but it uses jQuery which seems like is not working in WKWebViewanymore, so I have found solution without using jQuery.

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

Here is ViewController implementation that lets you be notified every AJAX request is completed in WKWebView:

这是 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)
        }
    }
}

Pretty standard implementation. There is a WKWebViewcreated programmatically. There is injected script that is loaded from script.jsfile.

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

And the most important part is script.jsfile:

最重要的部分是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);
};

userContentControllerdelegate method will be called every time there is AJAX request loaded. I'm passing there statusand responseURL, because this was what I needed in my case, but you can also get more informations about request. Here is the list of all properties and methods available: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

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

My solution is inspired by this answer written by @John Culviner: https://stackoverflow.com/a/27363569/3448282

我的解决方案的灵感来自@John Culviner 写的这个答案:https://stackoverflow.com/a/27363569/3448282

回答by Justin Michael

If you have control of the content inside the WkWebViewyou can send messages to your native app using window.webkit.messageHandlerswhenever you make an ajax request, which will be received as a WKScriptMessagethat can be processed by whatever you've designated as your WKScriptMessageHandler. The messages can contain whatever information you wish, and will be automatically converted into native objects/values in your Objective-C or Swift code.

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

If you don't have control over the content you can still do this by injecting your own JavaScript via a WKUserScriptto track ajax requests and send back messages using the method stated above.

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