web-dev-qa-db-fra.com

UIWebViewDelegate ne surveille pas XMLHttpRequest?

Est-il vrai que UIWebViewDelegate ne surveille pas les demandes effectuées à l'aide de XMLHttpRequest? Si oui, existe-t-il un moyen de surveiller ce type de demandes?

par exemple. UIWebViewDelegate ne détecte pas cela dans -(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();
51
Thys

Question interessante. 

Pour que cela fonctionne, il y a deux parties: un gestionnaire JavaScript et des méthodes de délégué UIWebView. En JavaScript, nous pouvons modifier les méthodes prototypes pour déclencher des événements lors de la création d'une demande AJAX. Avec notre délégué UIWebView, nous pouvons capturer ces événements.

Gestionnaire de JavaScript

Nous devons être avertis lorsqu'une demande AJAX est faite. J'ai trouvé la solution ici .

Dans notre cas, pour que le code fonctionne, j’ai mis le code JavaScript suivant dans une ressource appelée ajax_handler.js qui est fournie avec mon application.

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

Ce que cela va réellement faire est de changer l'emplacement du navigateur en un schéma d'URL composé (dans ce cas, mpAjaxHandle) avec des informations sur la requête effectuée. Ne vous inquiétez pas, notre délégué attrape cela et l'emplacement ne changera pas.

UIWebView delegate

Premièrement, nous devons lire notre fichier JavaScript. Je suggère de le stocker dans une variable statique. J'ai l'habitude d'utiliser + initialize.

static NSString *JSHandler;

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

Ensuite, nous voulons injecter ce code JavaScript avant le chargement d'une page afin de pouvoir recevoir tous les événements.

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

Enfin, nous voulons capturer l'événement. 

Puisque le schéma d'URL est constitué, nous ne voulons pas le suivre. Nous revenons NON et tout va bien.

#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;
}

J'ai créé un exemple de projet avec la solution mais je n'ai nulle part où l'héberger. Vous pouvez m'envoyer un message si vous pouvez l'héberger et je modifierai ce message en conséquence.

80
Mike Pulaski

Vous pouvez utiliser un protocole NSURL. Par exemple, si vous appelez XMLHttpRequest avec http://localhost/path, vous pouvez le gérer comme suit:

@interface YourProtocol: NSURLProtocol

Ensuite pour la mise en œuvre:

+ (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
{
}

Vous devez enregistrer le protocole comme suit:

    [NSURLProtocol registerClass:[YourProtocol class]];
20
George

En implémentant et en enregistrant une sous-classe de NSURLProtocol, vous pouvez capturer toutes les demandes de votre UIWebView. Cela peut être exagéré dans certains cas, mais si vous ne pouvez pas modifier le javascript en cours d’exécution, c’est votre meilleur choix.

Dans mon cas, j'ai besoin de capturer toute la requête et d'insérer un en-tête HTTP spécifique pour chacune d'entre elles ..__ J'ai réalisé cela en implémentant NSURLProtocol, en l'enregistrant à l'aide de registerClass et en répondant à YES dans ma sous-classe à + (BOOL) canInitWithRequest: Requête (NSURLRequest *) si la requête correspond aux URL qui m'intéressent . Vous devez ensuite implémenter les autres méthodes du protocole. Pour ce faire, vous pouvez utiliser un NSURLConnection, définir la classe de protocole en tant que délégué et rediriger le méthodes déléguées de NSURLConnection à NSURLProtocolClient

3
terrinecold

Cela semble être vrai. Il n'y a aucun moyen de surveiller ce que fait UIWebView au-delà de ce que UIWebViewDelegate fournit, à moins que vous ne trouviez peut-être une façon d'utiliser stringByEvaluatingJavaScriptFromString: pour injecter du code Javascript afin de faire ce dont vous avez besoin.

1
Anomie

Comme d'autres peuples l'ont mentionné, UIWebViewDelegate observe uniquement les modifications de window.location et d'iframe.src.

Si vous souhaitez uniquement utiliser le schéma url personnalisé d’iOS, vous pouvez tirer parti de <iframe> comme ceci.

Par exemple, si vous souhaitez appeler un schéma d'URL comme celui-ci

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

dans votre fichier 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});
0
Soyoes