UIWebViewDelegate 不监控 XMLHttpRequest?

发布于 2024-10-25 02:34:57 字数 510 浏览 2 评论 0原文

UIWebViewDelegate 是否不监视使用 XMLHttpRequest 发出的请求?如果是这样,有没有办法监控此类请求?

例如 UIWebViewDelegate 在 -(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSMutableURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; 中没有捕获到这一点;

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.google.com", true);

xhr.onreadystatechange=function() 
{
    if (xhr.readyState==4) 
    {
        alert(xhr.responseText);
    }
}

xhr.send();

Is it true that the UIWebViewDelegate does not monitor requests made by using a XMLHttpRequest? If so, is there a way to monitor these kind of requests?

e.g. UIWebViewDelegate does not catch this in -(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSMutableURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.google.com", true);

xhr.onreadystatechange=function() 
{
    if (xhr.readyState==4) 
    {
        alert(xhr.responseText);
    }
}

xhr.send();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

伊面 2024-11-01 02:34:57

有趣的问题。

有两个部分可以完成这项工作:JavaScript 处理程序和 UIWebView 委托方法。在 JavaScript 中,我们可以修改原型方法以在创建 AJAX 请求时触发事件。通过我们的 UIWebView 委托,我们可以捕获这些事件。

JavaScript Handler

我们需要在发出 AJAX 请求时收到通知。我在此处找到了解决方案。

在我们的例子中,为了使代码正常工作,我将以下 JavaScript 放入与我的应用程序捆绑在一起的名为 ajax_handler.js 的资源中。

var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
    window.location='mpAjaxHandler://' + this.url;
};

XMLHttpRequest.prototype.open = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempOpen.apply(this, arguments);
  s_ajaxListener.method = a;  
  s_ajaxListener.url = b;
  if (a.toLowerCase() == 'get') {
    s_ajaxListener.data = b.split('?');
    s_ajaxListener.data = s_ajaxListener.data[1];
  }
}

XMLHttpRequest.prototype.send = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempSend.apply(this, arguments);
  if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
  s_ajaxListener.callback();
}

这实际上会做的是将浏览器的位置更改为一些编写的 URL 方案(在本例中为 mpAjaxHandle),其中包含有关所发出请求的信息。别担心,我们的代表会抓住这一点,并且位置不会改变。

UIWebView Delegate

首先,我们需要读取 JavaScript 文件。我建议将其存储在静态变量中。我习惯使用+initialize。

static NSString *JSHandler;

+ (void)initialize {
    JSHandler = [[NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ajax_handler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:nil] retain];
}

接下来,我们希望在页面加载完成之前注入此 JavaScript,以便我们可以接收所有事件。

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [webView stringByEvaluatingJavaScriptFromString:JSHandler];
}

最后,我们想要捕获该事件。

由于 URL 方案是编造的,我们不想实际遵循它。我们返回NO,一切都很好。

#define CocoaJSHandler          @"mpAjaxHandler"

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if ([[[request URL] scheme] isEqual:CocoaJSHandler]) {
        NSString *requestedURLString = [[[request URL] absoluteString] substringFromIndex:[CocoaJSHandler length] + 3];

        NSLog(@"ajax request: %@", requestedURLString);
        return NO;
    }

    return YES;
}

我使用该解决方案创建了一个示例项目,但没有地方托管它。如果您可以主持,可以给我发消息,我将相应地编辑这篇文章。

Interesting question.

There are two parts to make this work: a JavaScript handler and UIWebView delegate methods. In JavaScript, we can modify prototype methods to trigger events when an AJAX request is created. With our UIWebView delegate, we can capture these events.

JavaScript Handler

We need to be notified when an AJAX request is made. I found the solution here.

In our case, to make the code work, I put the following JavaScript in a resource called ajax_handler.js which is bundled with my app.

var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
    window.location='mpAjaxHandler://' + this.url;
};

XMLHttpRequest.prototype.open = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempOpen.apply(this, arguments);
  s_ajaxListener.method = a;  
  s_ajaxListener.url = b;
  if (a.toLowerCase() == 'get') {
    s_ajaxListener.data = b.split('?');
    s_ajaxListener.data = s_ajaxListener.data[1];
  }
}

XMLHttpRequest.prototype.send = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempSend.apply(this, arguments);
  if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
  s_ajaxListener.callback();
}

What this will actually do is change the location of the browser to some made up URL scheme (in this case, mpAjaxHandle) with info about the request made. Don't worry, our delegate with catch this and the location won't change.

UIWebView Delegate

First, we need to read our JavaScript file. I suggest doing storing it in a static variable. I'm in the habit of using +initialize.

static NSString *JSHandler;

+ (void)initialize {
    JSHandler = [[NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ajax_handler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:nil] retain];
}

Next, we want to inject this JavaScript before a page is done loading so we can receive all events.

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [webView stringByEvaluatingJavaScriptFromString:JSHandler];
}

Finally, we want to capture the event.

Since the URL Scheme is made up, we don't want to actually follow it. We return NO and all is well.

#define CocoaJSHandler          @"mpAjaxHandler"

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if ([[[request URL] scheme] isEqual:CocoaJSHandler]) {
        NSString *requestedURLString = [[[request URL] absoluteString] substringFromIndex:[CocoaJSHandler length] + 3];

        NSLog(@"ajax request: %@", requestedURLString);
        return NO;
    }

    return YES;
}

I created a sample project with the solution but have nowhere to host it. You can message me if you can host it and I'll edit this post accordingly.

凉栀 2024-11-01 02:34:57

您可以使用 NSURLProtocol。例如,如果您使用 http://localhost/path 调用 XMLHttpRequest,您可以使用以下方式处理它:

@interface YourProtocol: NSURLProtocol

然后对于实现:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request 
{
    return [request.URL.host isEqualToString:@"localhost"];
}

+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

- (void) startLoading
{
    // Here you handle self.request 
}

- (void)stopLoading
{
}

您需要按如下方式注册协议:

    [NSURLProtocol registerClass:[YourProtocol class]];

You can use an NSURLProtocol. For instance if you call XMLHttpRequest with http://localhost/path you can handle it with the following:

@interface YourProtocol: NSURLProtocol

Then for the implementation:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request 
{
    return [request.URL.host isEqualToString:@"localhost"];
}

+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

- (void) startLoading
{
    // Here you handle self.request 
}

- (void)stopLoading
{
}

You need to register the protocol as follows:

    [NSURLProtocol registerClass:[YourProtocol class]];
南城追梦 2024-11-01 02:34:57

通过实现和注册 NSURLProtocol 的子类,您可以捕获来自 UIWebView 的所有请求。在某些情况下,这可能有点过头了,但如果您无法修改实际运行的 JavaScript,那么这是您最好的选择。

就我而言,我需要捕获所有请求并向每个请求插入特定的 HTTP 标头。
我通过实现 NSURLProtocol 来完成此操作,使用 registerClass 注册它,如果请求对应于我感兴趣的 URL,则在我的子类中对 + (BOOL)canInitWithRequest:(NSURLRequest *)request 回答 YES。
然后,您必须实现协议的其他方法,这可以通过使用 NSURLConnection、将协议类设置为委托并将 NSURLConnection 的委托方法重定向到 NSURLProtocolClient 来完成

By implementing and registering a subclass of NSURLProtocol you can capture all the request from your UIWebView. It may be overkill in some cases but if you are not able to modify the javascript actually being run it is your best bet.

In my case I need to capture all the request and insert a specific HTTP header to every one of them.
I have done this by implementing NSURLProtocol, registering it using registerClass and answering YES in my subclass to + (BOOL)canInitWithRequest:(NSURLRequest *)request if the request corresponds to the URLs I am interested in.
You then have to implement the other methods of the protocol this can be done by using an NSURLConnection, setting the protocol class as the delegate and redirecting the delegate methods of NSURLConnection to NSURLProtocolClient

零崎曲识 2024-11-01 02:34:57

看来确实是这样。除了 UIWebViewDelegate 提供的功能之外,没有其他方法可以监视 UIWebView 正在执行的操作,除非您可以找到一种使用 stringByEvaluatingJavaScriptFromString: 注入一些 Javascript 来执行您需要的操作的方法。

It does appear to be true. There is no way to monitor what a UIWebView is doing beyond what UIWebViewDelegate provides, unless perhaps you can figure out a way to use stringByEvaluatingJavaScriptFromString: to inject some Javascript to do what you need.

唱一曲作罢 2024-11-01 02:34:57

正如其他人提到的, UIWebViewDelegate 仅观察 window.location 和 iframe.src 的变化。

如果您只想使用 iOS 的自定义 url 方案,
您可以像这样利用

例如,如果你想

objc://my_action?arg1=1&arg2=2

在 js 文件中调用这样的 URL Scheme:

/**
* @param msg : path of your query
* @param data : arguments list of your query
*/
var call_url = function(msg, data){
    const scheme = "objc";
    var url = scheme + '://' + msg;
    if(data){
        var pstr = [];
        for(var k in data)
            if(typeof data[k] != 'function')
                pstr.push(encodeURIComponent(k)+"="+encodeURIComponent(data[k]));
        url += '?'+pstr.join('&');
    }
    var i = document.createElement("iframe");
    i.src = url;
    i.style.opacity=0;
    document.body.appendChild(i);
    setTimeout(function(){i.parentNode.removeChild(i)},200);
}

//when you call this custom url scheme
call_url ("my_action", {arg1:1,arg2:2});

As other peoples mentioned, UIWebViewDelegate observes changes of window.location and iframe.src only.

In case that you just want to use custom url scheme of iOS only,
you can take advantage of <iframe> like this way.

For example, if you want to call a URL Scheme like this

objc://my_action?arg1=1&arg2=2

in your js file:

/**
* @param msg : path of your query
* @param data : arguments list of your query
*/
var call_url = function(msg, data){
    const scheme = "objc";
    var url = scheme + '://' + msg;
    if(data){
        var pstr = [];
        for(var k in data)
            if(typeof data[k] != 'function')
                pstr.push(encodeURIComponent(k)+"="+encodeURIComponent(data[k]));
        url += '?'+pstr.join('&');
    }
    var i = document.createElement("iframe");
    i.src = url;
    i.style.opacity=0;
    document.body.appendChild(i);
    setTimeout(function(){i.parentNode.removeChild(i)},200);
}

//when you call this custom url scheme
call_url ("my_action", {arg1:1,arg2:2});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文