YTKNetwork 原始碼閱讀(二)-一次POST請求的完整歷程
寫在前面:
這篇文寫了什麼?
用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]; }
相關文章
- 完整的一次 HTTP 請求響應過程(二)HTTP
- 一次完整的 HTTP 請求過程HTTP
- 一次完整的HTTP請求過程HTTP
- scrapy-redis原始碼解讀之傳送POST請求Redis原始碼
- 完整的一次 HTTP 請求響應過程(一)HTTP
- 一次完整的HTTP請求HTTP
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- Vollery原始碼閱讀(二)原始碼
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- SpringMVC原始碼分析:POST請求中的檔案處理SpringMVC原始碼
- 原始碼閱讀:SDWebImage(二)——SDWebImageCompat原始碼Web
- goroutine排程原始碼閱讀筆記Go原始碼筆記
- 層層剖析一次 HTTP POST 請求事故HTTP
- python介面測試—post請求(二)Python
- 如何實現一個HTTP請求庫——axios原始碼閱讀與分析HTTPiOS原始碼
- YTKNetwork原始碼解析原始碼
- java post 請求Java
- iOS 同步請求 非同步請求 GET請求 POST請求iOS非同步
- RIPS原始碼閱讀記錄(二)原始碼
- 原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization原始碼
- 【詳解】ThreadPoolExecutor原始碼閱讀(二)thread原始碼
- 【面試】Web 頁面請求歷程面試Web
- 【PHP】一次請求過程的解析PHP
- postman(二):使用postman傳送get or post請求Postman
- TiDB 原始碼閱讀系列文章(二十三)Prepare/Execute 請求處理TiDB原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- zookeeper原始碼 — 五、處理寫請求過程原始碼
- jQueryAjax:$.post請求示例jQuery
- requests 模組 - post 請求
- post 請求工具類
- Spring原始碼閱讀——ClassPathXmlApplicationContext(二)Spring原始碼XMLAPPContext
- 逐行閱讀redux原始碼(二)combineReducersRedux原始碼
- Koa原始碼閱讀(二)上下文ctx原始碼
- basictracer-go原始碼閱讀二——SpanGo原始碼
- OKHttp原始碼學習同步請求和非同步請求(二)HTTP原始碼非同步
- get請求和post請求的區別