14

外部サービスへのAJAX呼び出しを行うUIWebviewがあります。オフラインの場合、これらのリクエストをキャッチしてローカルのjsonを返す必要があります。

NSURLProtocolを実装し、AJAXリクエストをキャッチできました。問題は、jqueryが常に0エラーコードを返すことです。

$.ajax({
  url: url,
  dataType: 'json',
  contentType: "application/json",
  success: function(jsonData){
    alert("success :");
  },
  error: function (request, status, error) {
    alert("failure :" + request.status );
  }

});

私はいつもrequest.status=0を受け取ります

プロトコルをテストするために、HTML内の画像をモックしようとしましたが、うまく機能します。

  • google.frからの画像へのHTMLリクエスト=>正常に機能します
  • AmazonのjsonへのAJAX呼び出し=>失敗

これが私の完全な実装です:

#import "EpubProtocol.h"

@implementation EpubProtocol

#pragma mark - NSURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    BOOL awsRequest = [self request:request contains:@"s3.amazonaws.com"];
    BOOL imgRequest = [self request:request contains:@"google.fr"];
    BOOL match = awsRequest || imgRequest;

    return match;
}


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


- (void)startLoading {
    NSURLRequest *request = [self request];

    //Mock Amazon call
    if([EpubProtocol request:request contains:@"s3.amazonaws.com"]) {
        NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"epub1" ofType:@"json"];
        NSData *data = [NSData dataWithContentsOfFile:path];

        [self mockRequest:request mimeType:@"application/json" data:data];
    }
    //Mock image call
    else if([EpubProtocol request:request contains:@"google.fr"]) {
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];

        [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.itespresso.fr/wp-content/gallery/yahoo/1-yahoo-logo.jpg"]] queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
            [self mockRequest:request mimeType:@"image/jpeg" data:data];
        }];
    }
}

- (void)stopLoading
{
    NSLog(@"Did stop loading");
}


#pragma mark - Request utils

+ (BOOL) request:(NSURLRequest*)request contains:(NSString*)domain {
    NSString *str = [[request URL] absoluteString];
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", domain];
    return [pred evaluateWithObject:str];
}


#pragma mark - Mock responses


-(void) mockRequest:(NSURLRequest*)request mimeType:(NSString*)mimeType data:(NSData*)data {
    id client = [self client];

    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[request URL] MIMEType:mimeType expectedContentLength:-1 textEncodingName:nil];

    [client URLProtocol:self didReceiveResponse:response
     cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [client URLProtocol:self didLoadData:data];
    [client URLProtocolDidFinishLoading:self];
}

@end
4

2 に答える 2

19

問題は、クロスドメインオリジンリクエストのために応答をブロックするWebkitに起因します。応答をモックするので、Access-Control-Allow-Originを強制する必要があります。

次に、応答のコンテンツタイプも強制する必要があります。

ここで魔法が起こります:

NSDictionary *headers = @{@"Access-Control-Allow-Origin" : @"*", @"Access-Control-Allow-Headers" : @"Content-Type"};
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:headers];

プロトコルの最終的な実装:

#import "EpubProtocol.h"

@implementation EpubProtocol

#pragma mark - NSURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    BOOL isAwsRequest = [self request:request contains:@"s3.amazonaws.com"];

    return isAwsRequest;
}

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

- (void)startLoading {
    NSURLRequest *request = [self request];

    //Mock Amazon call
    if([EpubProtocol request:request contains:@"s3.amazonaws.com"]) {
        NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"epub1" ofType:@"json"];
        NSData *data = [NSData dataWithContentsOfFile:path];

        [self mockRequest:request data:data];
    }
}

- (void)stopLoading
{
    NSLog(@"Did stop loading");
}


#pragma mark - Request utils

+ (BOOL) request:(NSURLRequest*)request contains:(NSString*)domain {
    NSString *str = [[request URL] absoluteString];
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", domain];
    return [pred evaluateWithObject:str];
}


#pragma mark - Mock responses


-(void) mockRequest:(NSURLRequest*)request data:(NSData*)data {
    id client = [self client];

    NSDictionary *headers = @{@"Access-Control-Allow-Origin" : @"*", @"Access-Control-Allow-Headers" : @"Content-Type"};
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:headers];

    [client URLProtocol:self didReceiveResponse:response
     cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [client URLProtocol:self didLoadData:data];
    [client URLProtocolDidFinishLoading:self];
}

@end

JSで特別なことは何もありません:

function loadJSONDoc()
{
  var url = "https://s3.amazonaws.com/youboox_recette/epub.json";

  $.ajax({
      url: url,
      dataType: 'json',
       contentType: "application/json",
      success: function(jsonData){
        alert('success');
        document.getElementById("myDiv").innerHTML='<p>'+$.param(jsonData)+'</p>';
      },
      error: function (request, status, error) {
        alert("failure :" + request.status );
      }
  });
}
于 2013-03-05T21:46:46.197 に答える
2

私はしばらく前に同様のことをしなければなりませんでした。

最初に、UIWebViewDelegateにajax呼び出しをキャッチさせることができるコードを見つけることができたので、Webアプリケーションの部分で次のようになりました。

//code to extend XMLHttpRequest, and have ajax call available in uiwebviewdelegate.

var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
    window.location=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();
}

次に、iOSでは、UIWebViewDelegate shouldStartLoadでNOを返します(私の状態は少し醜いです):

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] scheme] rangeOfString:@"https"].length > 0) 
    {
        return NO;
    }

    return YES;
}

その上、私は自分のプロトコルを登録しているでしょう:

[NSURLProtocol registerClass:[MyProtocol class]];

StartLoadの実装を使用します。また、canInitWithRequestをサブクラス化する必要があります

+ (BOOL)canInitWithRequest:(NSURLRequest *)request 
{
    if ([request.URL.scheme rangeOfString:@"https"].length > 0) 
    {
        return YES;
    }

    return NO;
}

要求に使用する必要があることをプロトコルに通知します。

そして、あなたがネットワークを持っているとき、登録を解除することを忘れないでください。

NSURLProtocolの実装に煩わされたくない場合は、私自身を使用できます:https ://github.com/bcharp/BOURLProtocol ;)

それが役に立てば幸い !

于 2013-03-05T12:54:51.267 に答える