YTKNetwork 源码阅读(二)-一次POST请求的完整历程

写在前面:

  1. 这篇文写了什么?
    用YTKNetwork走一次网络请求的完整历程。

  2. 我能看到哪些内容?
    以下是YTKNetwork官方文档的原话,说的是相比 AFNetworking,YTKNetwork 提供了以下更高级的功能,其中,后面打对号的部分是在这个历程中能看到的风景。

    • 支持按时间缓存网络请求内容 ✅
    • 支持按版本号缓存网络请求内容 ✅
    • 支持统一设置服务器和 CDN 的地址 ✅
    • 支持检查返回 JSON 内容的合法性 ✅
    • 支持文件的断点续传
    • 支持 block 和 delegate 两种模式的回调方式 ✅
    • 支持批量的网络请求发送,并统一设置它们的回调(实现在 YTKBatchRequest 类中)
    • 支持方便地设置有相互依赖的网络请求的发送,例如:发送请求 A,根据请求 A 的结果,选择性的发送请求 B 和 C,再根据 B 和 C 的结果,选择性的发送请求 D。(实现在 YTKChainRequest 类中)
    • 支持网络请求 URL 的 filter,可以统一为网络请求加上一些参数,或者修改一些路径。 ✅
    • 定义了一套插件机制,可以很方便地为 YTKNetwork 增加功能。猿题库官方现在提供了一个插件,可以在某些网络请求发起时,在界面上显示“正在加载”的 HUD。

旅程前的准备:(这一部分是YTKNetwork的基础用法,可以略过不看)

  1. 设置baseUrl
    //支持统一设置服务器和 CDN 的地址 ✅
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
    config.baseUrl = @"http://yuantiku.com";
    }

  2. 创建请求类

     // UserInfoRequest.m
     @interface UserInfoRequest () {
         NSString *_uid;
     }
     @end
    
     @implementation UserInfoRequest
    
     - (instancetype)initWithUid:(NSString *)uid {
         self = [super init];
         if (self) {
             _uid = uid;
         }
         return self;
     }
    
     - (NSString *)requestUrl {
         return @"/OwoccAppAPI/api/user";
     }
    
     - (YTKRequestMethod)requestMethod {
         return YTKRequestMethodPost;
     }
    
     - (id)requestArgument {
         return @{
                 @"uid": _uid,
                 };
     }
    

启程

  1. 开始一个新请求

     NSString *uid = @"1002";
     //1. 创建一个请求实例,uid是参数。这个请求是post。
     UserInfoRequest *userInfoReq = [[UserInfoRequest alloc] initWithUid:uid];
     //2. 便利请求方法,选择了block回调的方式。点进这个方法,去看看它的实现。
     [userInfoReq startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {
         NSLog(@"请求成功,返回数据:%@",request.responseString);
     } failure:^(YTKBaseRequest *request) {
         NSLog(@"请求失败");
     }];
    
  2. 请求便利方法

     - (void)startWithCompletionBlockWithSuccess:(void (^)(YTKBaseRequest *request))success
                                         failure:(void (^)(YTKBaseRequest *request))failure {
         //1. 设置好成功和失败的回调。请看下                                   
         [self setCompletionBlockWithSuccess:success failure:failure];
         //2. 开始
         [self start];
     }
    
     - (void)setCompletionBlockWithSuccess:(void (^)(YTKBaseRequest *request))success
                                 failure:(void (^)(YTKBaseRequest *request))failure {
         self.successCompletionBlock = success;
         self.failureCompletionBlock = failure;
     }
    
  3. 开始请求,首先看能不能从缓存拿数据

     - (void)start {
         //1. 如果忽略缓存,直接开始请求。如果不忽略,往下 走
         if (self.ignoreCache) {
             [super start];
             return;
         }
    
         //2. 支持按时间缓存网络请求内容 ✅。如果没有设置cacheTimeInSeconds,直接开始请求。如果设置了,往下 走
         if ([self cacheTimeInSeconds] < 0) {
             [super start];
             return;
         }
    
         //3. 支持按版本号缓存网络请求内容✅。
         long long cacheVersionFileContent = [self cacheVersionFileContent]; //已经缓存内容的版本号
         //如果cacheVersionFileContent和这个请求的版本号不同的话,直接开始请求。如果相同,往下 走
         if (cacheVersionFileContent != [self cacheVersion]) {
             [super start];
             return;
         }
    
         //4. 查看缓存是否存在。如果不存在,直接开始请求。如果存在,往下 走。缓存在沙盒中的路径是: /Library/LazyRequestCache/POST+完整链接地址+参数+应用版本号+敏感内容
         NSString *path = [self cacheFilePath];
         NSFileManager *fileManager = [NSFileManager defaultManager];
         if (![fileManager fileExistsAtPath:path isDirectory:nil]) {
             [super start];
             return;
         }
    
         //5. 检查缓存文件最后一次修改时间到现在的间隔。如果间隔大于我们设置的cacheTimeInSeconds直接开始请求。如果小于,往下 走
         int seconds = [self cacheFileDuration:path];
         if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {
             [super start];
             return;
         }
    
         //6. 从缓存中拿数据。如果为nil,直接开始请求。如果不为nil,往下 走
         _cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
         if (_cacheJson == nil) {
             [super start];
             return;
         }
    
         //7. 如果走到这里了,说明没有走接口,直接从缓存拿的数据。
          _dataFromCache = YES;//第一步
         [self requestCompleteFilter];//第二步
         //第三步:走请求完成的回调。支持 block 和 delegate 两种模式的回调方式 ✅
         YTKRequest *strongSelf = self;
         [strongSelf.delegate requestFinished:strongSelf];
         if (strongSelf.successCompletionBlock) {
             strongSelf.successCompletionBlock(strongSelf);
         }
         [strongSelf clearCompletionBlock];//把成功和失败的block置为nil
     }
    
  4. 请求真正开始(没能从缓存拿数据)

     /// append self to request queue
     - (void)start {
         [self toggleAccessoriesWillStartCallBack];//不用看
         //command 设计模式:把request传给接收器(这里就是YTKNetworkAgent)
         [[YTKNetworkAgent sharedInstance] addRequest:self];
     }
    
  5. YTKNetworkAgent的addRequest:方法

     - (void)addRequest:(YTKBaseRequest *)request {
         //1. 获取请求方法,这里是POST
         YTKRequestMethod method = [request requestMethod];
         //2. 组合完整链接地址: baseUrl + requestUrl
         //支持网络请求 URL 的 filter,可以统一为网络请求加上一些参数,或者修改一些路径。✅
         NSString *url = [self buildRequestUrl:request];
         //3. 请求参数
         id param = request.requestArgument;
         //4.
         AFConstructingBlock constructingBlock = [request constructingBodyBlock];
         //5. 请求序列化类型
         if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
             _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
         } else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
             _manager.requestSerializer = [AFJSONRequestSerializer serializer];
         }
         //6. 请求超时时间。可以自定义,默认60s
         _manager.requestSerializer.timeoutInterval = [request requestTimeoutInterval];
    
         //7. 如果需要的话,设置访问服务器的username和password
         NSArray *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
         if (authorizationHeaderFieldArray != nil) {
             [_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:(NSString *)authorizationHeaderFieldArray.firstObject
                                                                     password:(NSString *)authorizationHeaderFieldArray.lastObject];
         }
         
         //8. 如果需要的话,配置一些请求头的信息
         NSDictionary *headerFieldValueDictionary = [request requestHeaderFieldValueDictionary];
         if (headerFieldValueDictionary != nil) {
             for (id httpHeaderField in headerFieldValueDictionary.allKeys) {
                 id value = headerFieldValueDictionary[httpHeaderField];
                 if ([httpHeaderField isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {
                     [_manager.requestSerializer setValue:(NSString *)value forHTTPHeaderField:(NSString *)httpHeaderField];
                 } else {
                     YTKLog(@"Error, class of key/value in headerFieldValueDictionary should be NSString.");
                 }
             }
         }
    
         //9. 自定义url。
         NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
         if (customUrlRequest) {
             //注意: 如果自定义了url,会忽略请求类的requestUrl, requestArgument, requestMethod, requestSerializerType
             AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:customUrlRequest];
             [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
                 [self handleRequestResult:operation];
             } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                 [self handleRequestResult:operation];
             }];
             request.requestOperation = operation;
             operation.responseSerializer = _manager.responseSerializer;
             [_manager.operationQueue addOperation:operation];
         } else {
             //GET方法,我们这里的示例是POST,看下
             if (method == YTKRequestMethodGet) {
                 if (request.resumableDownloadPath) {
                     // add parameters to URL;
                     NSString *filteredUrl = [YTKNetworkPrivate urlStringWithOriginUrlString:url appendParameters:param];
    
                     NSURLRequest *requestUrl = [NSURLRequest requestWithURL:[NSURL URLWithString:filteredUrl]];
                     AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:requestUrl targetPath:request.resumableDownloadPath shouldResume:YES];
                     [operation setProgressiveDownloadProgressBlock:request.resumableDownloadProgressBlock];
                     [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
                         [self handleRequestResult:operation];
                     }                                failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                         [self handleRequestResult:operation];
                     }];
                     request.requestOperation = operation;
                     [_manager.operationQueue addOperation:operation];
                 } else {
                     request.requestOperation = [_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
                         [self handleRequestResult:operation];
                     }                                failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                         [self handleRequestResult:operation];
                     }];
                 }
             } else if (method == YTKRequestMethodPost) {
                 //10. 开始请求,用的是AFNetworking
                 if (constructingBlock != nil) {
                     request.requestOperation = [_manager POST:url parameters:param constructingBodyWithBlock:constructingBlock
                                                     success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                         [self handleRequestResult:operation];
                                                     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                 [self handleRequestResult:operation];
                             }];
                 } else {
                     request.requestOperation = [_manager POST:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
                         [self handleRequestResult:operation];
                     }                                 failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                         //12. 请求完成,处理请求的结果.(11请看下面)
                         [self handleRequestResult:operation];
                     }];
                 }
             } else if (method == YTKRequestMethodHead) {
                 request.requestOperation = [_manager HEAD:url parameters:param success:^(AFHTTPRequestOperation *operation) {
                     [self handleRequestResult:operation];
                 }                                 failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                     [self handleRequestResult:operation];
                 }];
             } else if (method == YTKRequestMethodPut) {
                 request.requestOperation = [_manager PUT:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
                     [self handleRequestResult:operation];
                 }                                failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                     [self handleRequestResult:operation];
                 }];
             } else if (method == YTKRequestMethodDelete) {
                 request.requestOperation = [_manager DELETE:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
                     [self handleRequestResult:operation];
                 }                                   failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                     [self handleRequestResult:operation];
                 }];
             } else if (method == YTKRequestMethodPatch) {
                 request.requestOperation = [_manager PATCH:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
                     [self handleRequestResult:operation];
                 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                     [self handleRequestResult:operation];
                 }];
             } else {
                 YTKLog(@"Error, unsupport method type");
                 return;
             }
         }
    
         YTKLog(@"Add request: %@", NSStringFromClass([request class]));
         //11. 把request存到_requestsRecord里面
         [self addOperation:request];
     }
    
  6. 处理请求的下来的数据handleRequestResult:

     - (void)handleRequestResult:(AFHTTPRequestOperation *)operation {
         NSString *key = [self requestHashKey:operation];
         //1. 把request从_requestsRecord拿出来
         YTKBaseRequest *request = _requestsRecord[key];
         YTKLog(@"Finished Request: %@", NSStringFromClass([request class]));
         //2. 如果request存在,接着往下走
         if (request) {
             // 支持检查返回 JSON 内容的合法性 ✅
             BOOL succeed = [self checkResult:request];
             if (succeed) {
                 //成功,走成功的回调
                 [request toggleAccessoriesWillStopCallBack];
                 [request requestCompleteFilter];
                 if (request.delegate != nil) {
                     [request.delegate requestFinished:request];
                 }
                 if (request.successCompletionBlock) {
                     request.successCompletionBlock(request);
                 }
                 [request toggleAccessoriesDidStopCallBack];
             } else {
                 //失败,走失败的回调
                 YTKLog(@"Request %@ failed, status code = %ld",
                         NSStringFromClass([request class]), (long)request.responseStatusCode);
                 [request toggleAccessoriesWillStopCallBack];
                 [request requestFailedFilter];
                 if (request.delegate != nil) {
                     [request.delegate requestFailed:request];
                 }
                 if (request.failureCompletionBlock) {
                     request.failureCompletionBlock(request);
                 }
                 [request toggleAccessoriesDidStopCallBack];
             }
         }
         //3. 把request从requestRecord移除出去
         [self removeOperation:operation];
         //4. 把成功和失败的block回调置为nil
         [request clearCompletionBlock];
     }
    

YTKNetwork 源码阅读(一)-整体框架

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容