UIWebViewDelegate не отслеживает XMLHttpRequest? - PullRequest
51 голосов
/ 18 марта 2011

Правда ли, что 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();

Ответы [ 5 ]

80 голосов
/ 21 апреля 2011

Интересный вопрос.

Эта работа состоит из двух частей: обработчик JavaScript и методы делегата UIWebView.В JavaScript мы можем модифицировать методы-прототипы для запуска событий при создании запроса AJAX.С нашим делегатом UIWebView мы можем захватить эти события.

Обработчик JavaScript

Мы должны быть уведомлены, когда сделан запрос 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;
}

Я создал пример проекта с решением, но некуда его разместить.Вы можете написать мне, если сможете разместить его, и я соответствующим образом отредактирую этот пост.

20 голосов
/ 24 июля 2012

Вы можете использовать NSURLProtocol.Например, если вы вызываете XMLHttpRequest с http://localhost/path, вы можете обработать его следующим образом:

@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]];
3 голосов
/ 31 января 2012

Реализуя и регистрируя подкласс NSURLProtocol, вы можете захватить весь запрос из вашего UIWebView.В некоторых случаях это может быть излишним, но если вы не можете изменить фактически выполняемый javascript, то это ваша лучшая ставка.

В моем случае мне нужно перехватить весь запрос и вставить каждый заголовок HTTPодин из них.Я сделал это, реализовав NSURLProtocol, зарегистрировав его с помощью registerClass и ответив YES в моем подклассе на запрос + (BOOL) canInitWithRequest: (NSURLRequest *), если запрос соответствует интересующим меня URL-адресам. Затем вам нужно реализовать другие методыпротокола это можно сделать с помощью NSURLConnection, установив класс протокола в качестве делегата и перенаправив методы делегата NSURLConnection в NSURLProtocolClient

1 голос
/ 14 апреля 2011

Это похоже на правду.Невозможно отслеживать, что делает UIWebView, помимо того, что предоставляет UIWebViewDelegate, если, возможно, вы не сможете найти способ использовать stringByEvaluatingJavaScriptFromString: для добавления некоторого Javascript для выполнения того, что вам нужно.

0 голосов
/ 19 февраля 2016

Как уже упоминалось, UIWebViewDelegate наблюдает только за изменениями в window.location и iframe.src.

В случае, если вы просто хотите использовать пользовательскую схему URL только для iOS, Вы можете воспользоваться преимуществом <iframe> таким образом.

Например, если вы хотите вызвать схему URL, как это

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

в вашем файле js:

/**
* @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});
...