美文网首页iOS开发技术分享
NSURLProtocol以及GYHttpMock学习记录

NSURLProtocol以及GYHttpMock学习记录

作者: 北纬3954 | 来源:发表于2017-08-11 18:51 被阅读139次

    NSURLProtocol的使用

    • 注册protocol。
    Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
    [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];
    

    以上方法适合于NSURLSession的网络请求,替换掉protocolClasses。如果使用的是NSURLConnection,则应使用下面的方法:
    [NSURLProtocol registerClass:[GYMockURLProtocol class]];

    • 实现protocol中必须实现的方法。
    + (BOOL)canInitWithRequest:(NSURLRequest *)request;
    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
    + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
    - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client;
    - (void)startLoading;
    - (void)stopLoading;
    

    canInitWithRequest是自己注册的URLProtocol类(比如此处的GYMockURLProtocol)的方法入口。只有这个方法返回YES时,才会执行下面的方法,返回NO时就直接运行系统的方法。

    canonicalRequestForRequest返回规范化的request。This method returns a canonical version of the given request。一般直接返回传入的request,而且不要在这里做规范化的操作,否则可能出现反复调用等奇怪现象。

    requestIsCacheEquivalent请求是使用缓存还是发起新请求。直接返回NO,设置每次都发起新请求。

    - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client;初始化方法,非必须实现。

    startLoading核心方法,真正的网络请求和mock方法这这个方法里面定义并执行。

    stopLoading网络请求完成后需要调用的方法。

    GYHttpMock使用示例:

    1. 添加需要mock的地址mockRequest(@"GET", @"https://httpbin.org/get").isUpdatePartResponseBody(YES);
    2. 发起网络请求
    NSURLSession * session = [NSURLSession sharedSession];
       [[session dataTaskWithURL:[NSURL URLWithString:@"https://httpbin.org/get"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
           if (error) {
               NSLog(@"%@",error);
           }else{
               NSLog(@"%@",data);
           }
       }] resume];
    

    第二步中发起的网络请求就会被GYHttpMock mock住。

    注意:如果第一步中不添加isUpdatePartResponseBody(YES),则第二步中发起的网络请求就会被GYHttpMock mock住,不会真的发起网络请求,只是返回code码是200的请求头。当添加了isUpdatePartResponseBody(YES)时才会发起网络请求。

    关于GYHttpMock的具体API见微信团队的博客

    代码详解:

    mockRequest方法会初始化GYHttpMock,添加两个hook。依据传入的URL和方法名创建GYMockRequest实例并保存。添加的两个hook是:GYNSURLConnectionHook,GYNSURLSessionHook。命名看起来是添加两个hook,其实是在完成上面说的方法注册和方法交换的功能。

    当发起网络请求时,会先调用GYHttpMockcanInitWithRequest方法。此时会检查第一步中是否有该url对应的GYMockRequest实例,如果有则返回YES,表明我们想要自己拦截请求或者修改response,GYHttpMockstartLoading等方法会被调用。如果返回NO就会把控制权交还给系统。

    GYHttpMock使用的是NSURLConnection发起真正的网络请求。

    无论是拦截请求,还是修改response,都应该主动调用

    - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;
    - (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;
    - (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;
    

    URLProtocol的方法,将最终数据返回给系统,系统会将数据传递给外部的网络请求者,比如AFNetworking或者NSURLSession的delegate。

    GYHttpMock还有一个值得一提的点是他使用了函数式编程的思想。GYMockRequestDSLGYMockResponseDSL里面通过block实现了函数式编程。实现方法是定义blcok属性,在block中完成操作,并最终在block中将self返回给调用者。
    举例如下:

    typedef GYMockResponseDSL *(^ResponseWithHeadersMethod)(NSDictionary *);
    @property (nonatomic, strong, readonly) ResponseWithHeaderMethod withHeader;
    - (ResponseWithHeadersMethod)withHeaders; {
        return ^(NSDictionary *headers) {
            for (NSString *header in headers) {
                NSString *value = [headers objectForKey:header];
                [self.response setHeader:header value:value];
            }
            return self;
        };
    }
    

    GYHttpMock修改网络返回数据需要调用者清楚返回数据的结构,并预先定义好和该结构相符的字典才能完成修改。具体的修改过程是通过 递归,层级检查返回的json结构,在合适的地方添加新内容。调用的方法如下:
    - (void)addEntriesFromDictionary:(NSDictionary *)dict to:(NSMutableDictionary *)targetDict

    相关文章

      网友评论

      • NickLin:NSURLSession 不用用到 method swizzing 吧
        https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411050-protocolclasses
        NickLin:你建立的NSURLSession 只會用那一個 URLSessionConfiguration ,那一個 URLSessionConfiguration 就只會有你自己加 NSURLProtocol ,跟幾個被調用幾個啥關係
        而且它是一個數組,是因為你可以寫多個,各自處理不同URL,canInitWithRequest return true 才會繼續往下走
        北纬3954:这个文档是说protocolClasses是个数组,添加进去的自定义的protocol会被调用。但如果添加了多个protocol,应该是只有最后添加进入的被调用。为了保险起见,使用method swizzing进行替换,保证自己写的protocol能被调用。

      本文标题: NSURLProtocol以及GYHttpMock学习记录

      本文链接:https://www.haomeiwen.com/subject/hjurrxtx.html