2013-03-05 5 views
14

외부 서비스에 AJAX 호출을하는 UIWebview가 있습니다. 오프라인 때 나는 논문 요청을 잡아서 로컬 JSON을 반환해야합니다. NSURLProtocol과 AJAX 호출을 조롱하는 방법?

가 나는 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 요청 =>가 아마존에 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 

답변

19

문제는 상호 도메인 원점 요청으로 인해 응답을 차단하는 웹킷에서 비롯됩니다. 우리가 응답을 조롱하므로 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]; 

프로토콜의 최종 구현 : 다음 JS 특수

#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 

아무것도 :

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); 
     } 
    }); 
} 
+0

좋은 직장. 매우 유용한 답변! –

+0

이것은 내 하루를 저장했습니다. 다른 사람들이 궁금해하는 분들을 위해 표준 HTTP 헤더를 사용하여 mimeType, 인코딩, 길이 등을 설정할 수 있습니다 (mimeType one, headers/statusCode). https://en.wikipedia.org/wiki/List_of_HTTP_header_fields. 헤더 사전에 추가하기 만하면됩니다. –

2

나는 얼마 전에 비슷한 일을해야했다.

먼저 내 웹 애플리케이션 부분에서 내가 가진 그래서 UIWebViewDelegate가, 아약스 전화를 잡을 수 코드를 찾기 위해 관리 :

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

그런 다음 아이폰 OS에서 나는 UIWebViewDelegate shouldStartLoad에서 NO 반환 (내 조건이었다 비트 추한) :

[NSURLProtocol registerClass:[MyProtocol class]]; 
,536 :

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

    return YES; 
} 

은 그 꼭대기에서 내 프로토콜을 등록 할 것

StartLoad 구현. 당신은 또한 요청에 대한 사용되어야하는 프로토콜을 말할 canInitWithRequest

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

    return NO; 
} 

를 서브 클래스해야한다.

그리고 네트워크를 가지고있을 때 등록을 잊지 마세요.

당신이 내 사용할 수있는 구현 NSURLProtocol 귀찮게하지 않으려는 경우 : https://github.com/bcharp/BOURLProtocol를)

는 희망이 도움이!

+0

난 여전히 0 상태를 확인 마법이 일어나는 곳 여기

입니다 그러나 나는 내가 가까웠다 고 생각한다. 나는 내 대답을 편집했다. – vdaubry

관련 문제