如何模拟AJAX调用NSURLProtocol? [英] How to mock AJAX call with NSURLProtocol?

查看:477
本文介绍了如何模拟AJAX调用NSURLProtocol?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的UIWebView这使得AJAX调用外部服务。当离线我需要赶论文请求并返回本地JSON。

我实现了一个NSURLProtocol,我设法抓住了AJAX请求,问题是jQuery的总是返回一个0错误code:

  $。阿贾克斯({
  网址:网址,
  数据类型:JSON,
  的contentType:应用/ JSON
  成功:函数(jsonData){
    警报(成功);
  },
  错误:函数(请求,状态,错误){
    警报(失败:+ request.status);
  }
 

});

我总是得到request.status = 0

要测试我的协议,我试图模拟图像我的HTML里面,它的伟大工程。

  • HTML请求从google.fr => 正常工作
  • 图像
  • 在AJAX亚马逊=调用一个JSON> 失败

下面是我的全面实施:

 #进口EpubProtocol.h

@implementation EpubProtocol

的#pragma马克 -  NSURLProtocol

+(BOOL)canInitWithRequest:(的NSURLRequest *)请求{
    BOOL awsRequest = [自我请求:请求包含:@s3.amazonaws.com];
    BOOL imgRequest = [自我请求:请求包含:@Google.fr的]。
    BOOL匹配= awsRequest || imgRequest;

    返回匹配;
}


+(的NSURLRequest *)canonicalRequestForRequest:(的NSURLRequest *)theRequest
{
    返回theRequest;
}


 - (无效)startLoading {
    的NSURLRequest *请求= [自我要求]

    //模拟亚马逊通话
    如果([EpubProtocol请求:请求包含:@s3.amazonaws.com]){
        的NSString *路径= [[一个NSBundle bundleForClass:self.class] pathForResource:@epub1ofType:@JSON];
        的NSData *数据= [NSData的dataWithContentsOfFile:路径]。

        [自mockRequest:请求MIMETYPE:@应用程序/ JSON数据:数据]
    }
    //模拟图像调用
    否则,如果([EpubProtocol请求:请求包含:@Google.fr的]){
        NSOperationQueue *队列= [[NSOperationQueue页头]初始化];

        [NSURLConnection的sendAsynchronousRequest:[的NSURLRequest requestWithURL:[NSURL URLWithString:@HTTP://www.ites$p$psso.fr/wp-content/gallery/yahoo/1-yahoo-logo.jpg]]队列:队列completionHandler :^(NSURLResponse *响应的NSData *数据,NSError *错误){
            [自mockRequest:请求MIMETYPE:@为image / jpeg的数据:数据]
        }];
    }
}

 - (无效)stopLoading
{
    的NSLog(@难道停止加载);
}


的#pragma马克 - 请求utils的

+(BOOL)要求:(的NSURLRequest *)请求包含:(的NSString *)域{
    的NSString *海峡= [[请求URL] absoluteString]。
    NS predicate * preD = NS predicate predicateWithFormat:@自包含[CD]%@,域]。
    返回[preD evaluateWithObject:STR]。
}


的#pragma马克 - 模拟响应


 - (无效)mockRequest:(的NSURLRequest *)要求MIMETYPE:(的NSString *)MIMETYPE数据:(NSData的*)数据{
    ID客户端= [自客户端]

    NSHTTPURLResponse *响应= [[NSHTTPURLResponse页头] initWithURL:请求URL] MIMETYPE:MIMETYPE expectedContentLength:-1 textEncodingName:无]

    [客户端URLProtocol:自didReceiveResponse:响应
     cacheStoragePolicy:NSURLCacheStorageNotAllowed]。
    [客户端URLProtocol:自didLoadData:数据]
    [客户端URLProtocolDidFinishLoading:个体经营];
}

@结束
 

解决方案

该问题来自WebKit的该块,因为跨域起源请求的响应。由于我们嘲笑的回应,我们必须强制访问控制 - 允许 - 产地。

然后,我们还需要强制内容类型的响应。

这里就是奇迹发生:

 的NSDictionary *头= @ {@访问控制 - 允许 - 起源:@*,@访问控制 - 允许 - 头:@内容类型};
NSHTTPURLResponse *响应= [[NSHTTPURLResponse页头] initWithURL:request.URL状态code:200 HTTPVersion:@1.1headerFields:头]
 

该协议的最终实施:

 #进口EpubProtocol.h

@implementation EpubProtocol

的#pragma马克 -  NSURLProtocol

+(BOOL)canInitWithRequest:(的NSURLRequest *)请求{
    BOOL isAwsRequest = [自我请求:请求包含:@s3.amazonaws.com];

    返回isAwsRequest;
}

+(的NSURLRequest *)canonicalRequestForRequest:(的NSURLRequest *)theRequest
{
    返回theRequest;
}

 - (无效)startLoading {
    的NSURLRequest *请求= [自我要求]

    //模拟亚马逊通话
    如果([EpubProtocol请求:请求包含:@s3.amazonaws.com]){
        的NSString *路径= [[一个NSBundle bundleForClass:self.class] pathForResource:@epub1ofType:@JSON];
        的NSData *数据= [NSData的dataWithContentsOfFile:路径]。

        [自mockRequest:请求数据:数据]
    }
}

 - (无效)stopLoading
{
    的NSLog(@难道停止加载);
}


的#pragma马克 - 请求utils的

+(BOOL)要求:(的NSURLRequest *)请求包含:(的NSString *)域{
    的NSString *海峡= [[请求URL] absoluteString]。
    NS predicate * preD = NS predicate predicateWithFormat:@自包含[CD]%@,域]。
    返回[preD evaluateWithObject:STR]。
}


的#pragma马克 - 模拟响应


 - (无效)mockRequest:(的NSURLRequest *)请求数据:(NSData的*)数据{
    ID客户端= [自客户端]

    的NSDictionary *头= @ {@访问控制 - 允许 - 起源:@*,@访问控制 - 允许 - 头:@内容类型};
    NSHTTPURLResponse *响应= [[NSHTTPURLResponse页头] initWithURL:request.URL状态code:200 HTTPVersion:@1.1headerFields:头]

    [客户端URLProtocol:自didReceiveResponse:响应
     cacheStoragePolicy:NSURLCacheStorageNotAllowed]。
    [客户端URLProtocol:自didLoadData:数据]
    [客户端URLProtocolDidFinishLoading:个体经营];
}

@结束
 

没有什么特别的JS:

 函数loadJSONDoc()
{
  VAR URL =htt​​ps://s3.amazonaws.com/youboox_recette/epub.json;

  $阿贾克斯({
      网址:网址,
      数据类型:JSON,
       的contentType:应用/ JSON
      成功:函数(jsonData){
        警报(成功);
        的document.getElementById(myDiv)的innerHTML ='&其中p为H.;'+ $参数(jsonData)+'&所述; / P>';
      },
      错误:函数(请求,状态,错误){
        警报(失败:+ request.status);
      }
  });
}
 

I have UIWebview that makes AJAX calls to external services. When offline i need to catch theses requests and return local json.

I implemented a NSURLProtocol and i manage to catch the AJAX request, the problem is jquery always return a 0 error code :

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

});

I always get a request.status = 0

To test my protocol I tried to mock an image inside my html and it works great.

  • HTML request to an image from google.fr => works fine
  • AJAX call to a json on amazon => fails

Here is my full implementation :

#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

解决方案

The problem comes from webkit which blocks the response because of cross domain origin request. Since we mock the response we have to force the Access-Control-Allow-Origin.

Then we also need to force the content-type of the response.

Here is where the magic happens :

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

The final implementation of the protocol :

#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

Nothing special in the 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 );
      }
  });
}

这篇关于如何模拟AJAX调用NSURLProtocol?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆