写在前面:
这篇文写了什么?
用YTKNetwork走一次网络请求的完整历程。-
我能看到哪些内容?
以下是YTKNetwork官方文档的原话,说的是相比 AFNetworking,YTKNetwork 提供了以下更高级的功能,其中,后面打对号的部分是在这个历程中能看到的风景。- 支持按时间缓存网络请求内容 ✅
- 支持按版本号缓存网络请求内容 ✅
- 支持统一设置服务器和 CDN 的地址 ✅
- 支持检查返回 JSON 内容的合法性 ✅
- 支持文件的断点续传
- 支持 block 和 delegate 两种模式的回调方式 ✅
- 支持批量的网络请求发送,并统一设置它们的回调(实现在 YTKBatchRequest 类中)
- 支持方便地设置有相互依赖的网络请求的发送,例如:发送请求 A,根据请求 A 的结果,选择性的发送请求 B 和 C,再根据 B 和 C 的结果,选择性的发送请求 D。(实现在 YTKChainRequest 类中)
- 支持网络请求 URL 的 filter,可以统一为网络请求加上一些参数,或者修改一些路径。 ✅
- 定义了一套插件机制,可以很方便地为 YTKNetwork 增加功能。猿题库官方现在提供了一个插件,可以在某些网络请求发起时,在界面上显示“正在加载”的 HUD。
旅程前的准备:(这一部分是YTKNetwork的基础用法,可以略过不看)
设置baseUrl
//支持统一设置服务器和 CDN 的地址 ✅
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
config.baseUrl = @"http://yuantiku.com";
}-
创建请求类
// 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, }; }
启程
-
开始一个新请求
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(@"请求失败"); }];
-
请求便利方法
- (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; }
-
开始请求,首先看能不能从缓存拿数据
- (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 }
-
请求真正开始(没能从缓存拿数据)
/// append self to request queue - (void)start { [self toggleAccessoriesWillStartCallBack];//不用看 //command 设计模式:把request传给接收器(这里就是YTKNetworkAgent) [[YTKNetworkAgent sharedInstance] addRequest:self]; }
-
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]; }
-
处理请求的下来的数据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]; }