web-dev-qa-db-ja.com

UIWebViewDelegateはXMLHttpRequestを監視していませんか?

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();
51
Thys

興味深い質問。

これを機能させるには、JavaScriptハンドラーとUIWebViewデリゲートメソッドの2つの部分があります。 JavaScriptでは、AJAX要求が作成されたときにイベントをトリガーするようにプロトタイプメソッドを変更できます。UIWebViewデリゲートを使用すると、これらのイベントをキャプチャできます。

JavaScriptハンドラー

AJAXリクエストが行われたときに通知を受ける必要があります。解決策が見つかりました here

この場合、コードを機能させるために、アプリにバンドルされているajax_handler.jsというリソースに次のJavaScriptを配置します。

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デリゲート

最初に、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;
}

ソリューションを使用してサンプルプロジェクトを作成しましたが、ホストする場所がありません。ホストできたらメッセージを送ってください。それに応じてこの投稿を編集します。

80
Mikey

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]];
20
George

NSURLProtocolのサブクラスを実装および登録することにより、UIWebViewからすべてのリクエストをキャプチャできます。場合によってはやり過ぎかもしれませんが、実際に実行されているJavaScriptを変更できない場合は、最善の方法です。

私の場合、すべてのリクエストをキャプチャし、それらのすべてに特定のHTTPヘッダーを挿入する必要があります。 NSURLProtocolを実装し、registerClassを使用してそれを登録し、サブクラスでYES(+)(BOOL)canInitWithRequest:(NSURLRequest *)requestに、興味のあるURLに対応する場合は答えます。プロトコルのNSURLConnectionを使用して、プロトコルクラスをデリゲートとして設定し、NSURLConnectionのデリゲートメソッドをNSURLProtocolClientにリダイレクトすることで実行できます。

3
terrinecold

それは本当のようです。おそらくあなたがstringByEvaluatingJavaScriptFromString:必要なことを行うためにJavascriptを挿入します。

1
Anomie

他の人々が言及したように、UIWebViewDelegateはwindow.locationとiframe.srcの変更のみを監視します。

IOSのカスタムURLスキームのみを使用する場合は、<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});
0
Soyoes