為什麼要對AFNetworking進行多一層的封裝
對於AFNetworking進行二次封裝是很有必要的。事實上,在專案中大量使用第三方網路庫是有風險的,因為網路請求的使用遍佈整個應用,而一旦該對應的網路庫不再更新維護,或者我們有意願的去更換網路框架,修改將會有著巨大的難以承受的工作量。
這一個框架與其他框架有什麼不同
- 本框架基於AFNetworking3.0的版本進行封裝,面向更新的版本。
- 為網路請求的任務管理做了大量的工作,使得下載上傳,或者其他使用環境下的任務管理得以更輕鬆的實現。
- 反其道而行,專為Post請求作出了快取處理,這一部分快取處理的使用與否,由使用者自行決定。
- 自定義Get請求的快取策略,以時間為基準,嚴格把控快取的有效性。
介面設計
請求
這裡使用了新建NS_ENUM的方式來統一Post和Get介面。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * 請求方式 */ typedef NS_ENUM(NSInteger, CBRequestType) { /** * POST方式來進行請求 */ CBPOSTRequest = 1 << 0, /** * GET方式來進行請求 */ CBGETRequest = 1 << 1 }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * 統一請求介面 * * @param url 請求路徑 * @param params 拼接引數 * @param httpMethod 請求方式(0為POST,1為GET) * @param useCache 是否使用快取 * @param progressBlock 進度回撥 * @param successBlock 成功回撥block * @param failBlock 失敗回撥block * * @return 返回的物件中可取消請求 */ + (CBURLSessionTask *)requestWithUrl:(NSString *)url params:(NSDictionary *)params useCache:(BOOL)useCache httpMedthod:(CBRequestType)httpMethod progressBlock:(CBNetWorkingProgress)progressBlock successBlock:(CBResponseSuccessBlock)successBlock failBlock:(CBResponseFailBlock)failBlock; |
從上面的程式碼我們可以看到,引數useCache決定著你是否使用Post請求的快取功能,而即便將其設定為NO,依然會自動開啟Get請求的快取功能。
資料解析方式
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * 資料序列方式 */ typedef NS_ENUM(NSInteger, CBSerializerType) { /** * HTTP方式來進行請求或響應 */ CBHTTPSerializer = 1 << 0, /** * JSON方式來進行請求或響應 */ CBJSONSerializer = 1 << 1 }; |
1 2 3 4 5 6 7 8 |
/** * 更新請求或者返回資料的解析方式(0為HTTP模式,1為JSON模式) * * @param requestType 請求資料解析方式 * @param responseType 返回資料解析方式 */ + (void)updateRequestSerializerType:(CBSerializerType)requestType responseSerializer:(CBSerializerType)responseType; |
上面的NS_ENUM聯合上面的方法可以更改資料解析方式,更具靈活性。
檔案上傳,下載
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * 檔案上傳介面 * * @param url 傳檔案介面地址 * @param uploadingFile 上傳檔案路徑 * @param progressBlock 上傳進度 * @param successBlock 成功回撥 * @param failBlock 失敗回撥 * * @return 返回的物件中可取消請求 */ + (CBURLSessionTask *)uploadFileWithUrl:(NSString *)url uploadingFile:(NSString *)uploadingFile progressBlock:(CBNetWorkingProgress)progressBlock successBlock:(CBResponseSuccessBlock)successBlock failBlock:(CBResponseFailBlock)failBlock; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * 檔案下載介面 * * @param url 下載檔案介面地址 * @param saveToPath 儲存目錄 * @param progressBlock 下載進度 * @param successBlock 成功回撥 * @param failBlock 下載回撥 * * @return 返回的物件可取消請求 */ + (CBURLSessionTask *)downloadWithUrl:(NSString *)url saveToPath:(NSString *)saveToPath progressBlock:(CBNetWorkingProgress)progressBlock successBlock:(CBResponseSuccessBlock)successBlock failBlock:(CBResponseFailBlock)failBlock; |
針對檔案的下載和上傳的做出專門的設計。
任務管理
由上面的方法中,我們可以看到每一個方法都返回了一個任務物件CBURLSessionTask,這個物件繼承於NSURLSessionTask,如此我們可以直接的取到每次進行任務請求的物件,直接對其進行管理。但不止於此,我仍然寫了以下的幾個介面來更快捷更集中的對其進行管理。
1 2 3 4 |
/** * 取消所有請求 */ + (void)cancelAllRequest; |
1 2 3 4 5 6 |
/** * 根據url取消請求 * * @param url 請求url */ + (void)cancelRequestWithURL:(NSString *)url; |
通過這兩個方法,我們可以直接取消所有的請求,或者取消單個連結對應的請求。全域性性的執行取消操作,更方便。
快取處理
Post快取處理
由於蘋果官方的NSURLCache不支援Post請求的快取處理,所有這一部分的快取處理,我自己通過歸檔的方式來進行管理。
主要的方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
(id)getCacheResponseWithURL:(NSString *)url params:(NSDictionary *)params { id cacheData = nil; if (url) { NSString *directoryPath = DIRECTORYPATH; NSString *originString = [NSString stringWithFormat:@"%@+%@",url,params]; NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]]; NSData *data = [[NSFileManager defaultManager] contentsAtPath:path]; if (data) { cacheData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; } } return cacheData; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
+ (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request params:(NSDictionary *)params { if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) { NSString *directoryPath = DIRECTORYPATH; NSError *error = nil; if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error]; } NSString *originString = [NSString stringWithFormat:@"%@+%@",request.URL.absoluteString,params]; NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]]; NSDictionary *dict = (NSDictionary *)responseObject; NSData *data = nil; if ([dict isKindOfClass:[NSData class]]) { data = responseObject; } else { data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error]; } if (data && error == nil) { [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil]; } } } + (NSString *)md5:(NSString *)string { if (string == nil || [string length] == 0) { return nil; } unsigned char digest[CC_MD5_DIGEST_LENGTH], i; CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest); NSMutableString *ms = [NSMutableString string]; for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { [ms appendFormat:@"%02x", (int)(digest[i])]; } return [ms copy]; } |
值得注意的是,每一次進行快取,都通過HASH的方式對快取進行了加密,從而達到唯一快取和更加安全的目的。
Get請求的自定義
按道理,官方已經對於Get請求進行了很完善的快取處理,為什麼我還要自行處理呢?事實上,我並沒有自定義這一部分的處理,我僅對於NSURLCache加多了一層封裝,從而達到自定義策略的目的,主要的方法如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
- (id)cachedResponseForRequest:(NSURLRequest *)request { NSCachedURLResponse *cachedResponse = [super cachedResponseForRequest:request]; if (cachedResponse) { NSDate *cacheDate = cachedResponse.userInfo[CBURLCacheExpirationKey]; NSDate *cacheExpirationDate = [cacheDate dateByAddingTimeInterval:CBURLCacheExpirationInterval]; if ([cacheExpirationDate compare:[NSDate date]] == NSOrderedAscending) { [self removeCachedResponseForRequest:request]; return nil; } } id responseObj = [NSJSONSerialization JSONObjectWithData:cachedResponse.data options:NSJSONReadingAllowFragments error:nil]; return responseObj; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (void)storeCachedResponse:(id)response responseObjc:(id)responseObjc forRequest:(NSURLRequest *)request { NSData *data = [NSJSONSerialization dataWithJSONObject:responseObjc options:NSJSONWritingPrettyPrinted error:nil]; NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; userInfo[CBURLCacheExpirationKey] = [NSDate date]; NSCachedURLResponse *modifiedCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:userInfo storagePolicy:0]; [super storeCachedResponse:modifiedCachedResponse forRequest:request]; } |