NSURLProtocol以及GYHttpMock學(xué)習(xí)記錄

NSURLProtocol的使用

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

以上方法適合于NSURLSession的網(wǎng)絡(luò)請求,替換掉protocolClasses。如果使用的是NSURLConnection,則應(yīng)使用下面的方法:
[NSURLProtocol registerClass:[GYMockURLProtocol class]];

  • 實現(xiàn)protocol中必須實現(xiàn)的方法。
+ (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時,才會執(zhí)行下面的方法,返回NO時就直接運行系統(tǒng)的方法。

canonicalRequestForRequest返回規(guī)范化的request。This method returns a canonical version of the given request。一般直接返回傳入的request,而且不要在這里做規(guī)范化的操作,否則可能出現(xiàn)反復(fù)調(diào)用等奇怪現(xiàn)象。

requestIsCacheEquivalent請求是使用緩存還是發(fā)起新請求。直接返回NO,設(shè)置每次都發(fā)起新請求。

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client;初始化方法,非必須實現(xiàn)。

startLoading核心方法,真正的網(wǎng)絡(luò)請求和mock方法這這個方法里面定義并執(zhí)行。

stopLoading網(wǎng)絡(luò)請求完成后需要調(diào)用的方法。

GYHttpMock使用示例:

  1. 添加需要mock的地址mockRequest(@"GET", @"https://httpbin.org/get").isUpdatePartResponseBody(YES);
  2. 發(fā)起網(wǎng)絡(luò)請求
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];

第二步中發(fā)起的網(wǎng)絡(luò)請求就會被GYHttpMock mock住。

注意:如果第一步中不添加isUpdatePartResponseBody(YES),則第二步中發(fā)起的網(wǎng)絡(luò)請求就會被GYHttpMock mock住,不會真的發(fā)起網(wǎng)絡(luò)請求,只是返回code碼是200的請求頭。當(dāng)添加了isUpdatePartResponseBody(YES)時才會發(fā)起網(wǎng)絡(luò)請求。

關(guān)于GYHttpMock的具體API見微信團隊的博客。

代碼詳解:

mockRequest方法會初始化GYHttpMock,添加兩個hook。依據(jù)傳入的URL和方法名創(chuàng)建GYMockRequest實例并保存。添加的兩個hook是:GYNSURLConnectionHook,GYNSURLSessionHook。命名看起來是添加兩個hook,其實是在完成上面說的方法注冊和方法交換的功能。

當(dāng)發(fā)起網(wǎng)絡(luò)請求時,會先調(diào)用GYHttpMockcanInitWithRequest方法。此時會檢查第一步中是否有該url對應(yīng)的GYMockRequest實例,如果有則返回YES,表明我們想要自己攔截請求或者修改response,GYHttpMockstartLoading等方法會被調(diào)用。如果返回NO就會把控制權(quán)交還給系統(tǒng)。

GYHttpMock使用的是NSURLConnection發(fā)起真正的網(wǎng)絡(luò)請求。

無論是攔截請求,還是修改response,都應(yīng)該主動調(diào)用

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

URLProtocol的方法,將最終數(shù)據(jù)返回給系統(tǒng),系統(tǒng)會將數(shù)據(jù)傳遞給外部的網(wǎng)絡(luò)請求者,比如AFNetworking或者NSURLSession的delegate。

GYHttpMock還有一個值得一提的點是他使用了函數(shù)式編程的思想。GYMockRequestDSLGYMockResponseDSL里面通過block實現(xiàn)了函數(shù)式編程。實現(xiàn)方法是定義blcok屬性,在block中完成操作,并最終在block中將self返回給調(diào)用者。
舉例如下:

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修改網(wǎng)絡(luò)返回數(shù)據(jù)需要調(diào)用者清楚返回數(shù)據(jù)的結(jié)構(gòu),并預(yù)先定義好和該結(jié)構(gòu)相符的字典才能完成修改。具體的修改過程是通過 遞歸,層級檢查返回的json結(jié)構(gòu),在合適的地方添加新內(nèi)容。調(diào)用的方法如下:
- (void)addEntriesFromDictionary:(NSDictionary *)dict to:(NSMutableDictionary *)targetDict

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,724評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,376評論 25 708
  • AFHTTPRequestOperationManager 網(wǎng)絡(luò)傳輸協(xié)議UDP、TCP、Http、Socket、X...
    Carden閱讀 5,349評論 0 12
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 12,557評論 6 13
  • ThreadLocal 內(nèi)置N個ThreadLocalMap 根據(jù)當(dāng)前Thread對象獲取到Map,key是當(dāng)前T...
    師襄閱讀 198評論 0 0

友情鏈接更多精彩內(nèi)容