2013-03-05 31 views
14

Tôi có UIWebview giúp thực hiện cuộc gọi AJAX đến các dịch vụ bên ngoài. Khi offline tôi cần nắm bắt các yêu cầu và trả về json cục bộ.Làm thế nào để thử gọi AJAX với NSURLProtocol?

Tôi thực hiện một NSURLProtocol và tôi quản lý để nắm bắt những yêu cầu AJAX, vấn đề được jquery luôn trả về một mã 0 lỗi:

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

});

tôi luôn có được một request.status = 0

Để kiểm tra giao thức của tôi, tôi đã cố gắng để thử một hình ảnh bên html của tôi và nó hoạt động tuyệt vời.

  • yêu cầu HTML để một hình ảnh từ google.fr =>hoạt động tốt
  • gọi AJAX đến một json trên amazon =>không

Đây là thực hiện đầy đủ của tôi:

#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 

Trả lời

19

Sự cố đến từ webkit chặn phản hồi vì yêu cầu xuất xứ miền chéo. Vì chúng ta giả định phản hồi, chúng ta phải bắt buộc Access-Control-Allow-Origin.

Sau đó, chúng tôi cũng cần buộc loại nội dung của phản hồi.

Đây là nơi sự kỳ diệu xảy ra:

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

Việc thực hiện cuối cùng của giao thức:

#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 

Không có gì đặc biệt trong 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); 
     } 
    }); 
} 
+0

Công việc tuyệt vời. Câu trả lời rất hữu ích! –

+0

Điều này đã cứu ngày của tôi. BTW cho mọi người tự hỏi, vì bạn không thể gọi cả hai initializers (mimeType một, so với tiêu đề/statusCode một) bạn CÓ THỂ thiết lập mimeType, mã hóa, chiều dài, vv bằng cách sử dụng tiêu đề HTTP tiêu chuẩn: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields. Chỉ cần thêm vào từ điển đầu trang của bạn –

2

Tôi đã phải làm một công cụ tương tự cách đây một thời gian.

Trước tiên tôi quản lý để tìm mã mà có thể làm cho UIWebViewDelegate bắt cuộc gọi ajax, vì vậy trong phần webapp của tôi, tôi có:

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

Sau đó, trong iOS Tôi trở NO trong UIWebViewDelegate shouldStartLoad (tình trạng của tôi là một chút xấu xí):

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

    return YES; 
} 

trong đầu đó tôi sẽ phải đăng ký giao thức của tôi:

[NSURLProtocol registerClass:[MyProtocol class]]; 

Với triển khai Bắt đầu tải. Bạn cũng cần phải có phân lớp các canInitWithRequest

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

    return NO; 
} 

Để nói giao thức mà nó phải được sử dụng cho yêu cầu.

Và đừng quên hủy đăng ký khi bạn có mạng.

Nếu bạn không muốn bận tâm với NSURLProtocol thực hiện bạn có thể sử dụng riêng tôi: https://github.com/bcharp/BOURLProtocol;)

Hy vọng nó sẽ giúp!

+0

tôi vẫn có được một tình trạng 0 nhưng tôi nghĩ tôi gần rồi. Tôi đã chỉnh sửa câu trả lời của mình. – vdaubry

Các vấn đề liên quan