一. AFNetworking簡(jiǎn)單介紹
通過(guò)前面學(xué)習(xí)的HTTP協(xié)議的基本知識(shí),GET/POST請(qǐng)求的區(qū)別,NSURLConnection 和 NSURLSession的使用,已經(jīng)基本了解了網(wǎng)絡(luò)請(qǐng)求的方法,但是相對(duì)使用比較麻煩,AFNetworking是對(duì)NSURLConnection 和 NSURLSession的封裝,使網(wǎng)絡(luò)請(qǐng)求更加簡(jiǎn)單輕松,是一款非常有用的第三方框架。AFNetworking3.0以后移除了對(duì)NSURLConnection的支持。
看一下AFNetworking 歷史版本 以及框架


這些在GitHub上都可以看到,可以去GitHub上查看。
二. AFNetworking使用
1. GET請(qǐng)求
AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
NSDictionary *dict = @{
@"username":@"520it",
@"pwd":@"520it"
};
// parameters 參數(shù)字典
[manager GET:@"http://120.25.226.186:32812/login" parameters:dict progress:^(NSProgress * _Nonnull downloadProgress) {
//進(jìn)度
//進(jìn)度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// task 我們可以通過(guò)task拿到響應(yīng)頭
// responseObject:請(qǐng)求成功返回的響應(yīng)結(jié)果(AFN內(nèi)部已經(jīng)把響應(yīng)體轉(zhuǎn)換為OC對(duì)象,通常是字典或數(shù)組)
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// error 錯(cuò)誤信息
}];
2. POST請(qǐng)求
AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
NSDictionary *dict = @{
@"username":@"520it",
@"pwd":@"520it"
};
[manager POST:@"http://120.25.226.186:32812/login" parameters:dict progress:^(NSProgress * _Nonnull downloadProgress) {
// 進(jìn)度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 請(qǐng)求成功
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 請(qǐng)求失敗
}];
注意:我們發(fā)現(xiàn)GET和POST請(qǐng)求一模一樣,僅僅換了一個(gè)名字,GET請(qǐng)求也可以將參數(shù)放在字典中,也可以將參數(shù)拼接在url之后parameters傳nil。另外不需要開啟Task,因?yàn)锳FN內(nèi)部已經(jīng)幫我們開啟了
另外:AFN默認(rèn)會(huì)把服務(wù)器返回給我們的數(shù)據(jù)當(dāng)做是JSNO數(shù)據(jù),并且AFN內(nèi)部已經(jīng)把響應(yīng)體JSON數(shù)據(jù)轉(zhuǎn)換為OC對(duì)象,通常是字典或數(shù)組。
那么如果服務(wù)器返回的XML呢?這時(shí)我們需要自己設(shè)置AFN解析方式
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
返回的是NSXMLParser,需要我們自己解析
如果返回的數(shù)據(jù)既不是JSON也不是XML那么需要設(shè)置
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
AFN默認(rèn)接收的ContentTypes 有以下三種

如果服務(wù)器返回的ContentType不是這三種中的一種,我們就需要設(shè)置
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
也可以直接在AFN源碼中添加(不建議使用,這種方式比較隱蔽,當(dāng)更新過(guò)AFN之后這里會(huì)還原,出現(xiàn)問題比較難找)

3. 文件下載
// 1.創(chuàng)建一個(gè)管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2. 創(chuàng)建請(qǐng)求對(duì)象
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_03.png"];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
// 3. 下載文件
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
// downloadProgress.completedUnitCount 當(dāng)前下載大小
// downloadProgress.totalUnitCount 總大小
NSLog(@"%f", 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
// targetPath 臨時(shí)存儲(chǔ)地址
NSLog(@"targetPath:%@",targetPath);
NSString *path =[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingPathComponent:response.suggestedFilename];
NSURL *url = [NSURL fileURLWithPath:filePath];
NSLog(@"path:%@",filePath);
// 返回url 我們想要存儲(chǔ)的地址
// response 響應(yīng)頭
return url;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
// 下載完成之后調(diào)用
// response 響應(yīng)頭
// filePath 下載存儲(chǔ)地址
NSLog(@"filePath:%@",filePath);
}];
// 需要手動(dòng)開啟
[downloadTask resume];
注意:
1. 下載文件需要獲取NSURLSessionDownloadTask對(duì)象手動(dòng)開啟
2. 第一個(gè)block塊:downloadProgress 有兩個(gè)屬性completedUnitCount(已經(jīng)下載文件大?。?、totalUnitCount(文件總大?。?。
3. 第二個(gè)block塊:需要返回一個(gè)url,表示想要將文件存儲(chǔ)的地方。targetPath:表示臨時(shí)存儲(chǔ)地址在tmp臨時(shí)文件中。response:響應(yīng)頭 可以拿到一些文件信息
4. 第三個(gè)block塊:下載完成之后調(diào)用。response:響應(yīng)頭。filePath:文件存儲(chǔ)地址,與第二個(gè)block塊中返回的url是一個(gè)地址
4. 文件上傳
關(guān)于文件上傳使用AFN就簡(jiǎn)單多了,也不需要我們?nèi)テ唇诱?qǐng)求體和請(qǐng)求文件參數(shù)啦,AFN內(nèi)部已經(jīng)幫我們拼接好了
方法一:formData 添加data形式數(shù)據(jù)
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *url =@"http://120.25.226.186:32812/upload";
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
// formData 將要上傳的數(shù)據(jù)
UIImage *image =[UIImage imageNamed:@"123"];
NSData *data =UIImagePNGRepresentation(image);
// 方法一
/**
data:上傳文件二進(jìn)制數(shù)據(jù)
name:接口的名字
fileName:文件上傳到服務(wù)器之后叫什么名字
mineType:上傳文件的類型,可以上傳任意二進(jìn)制mineType.
*/
[formData appendPartWithFileData:data name:@"file" fileName:@"123.png" mimeType:@"image/png"];
// 方法二
/**
data:上傳文件二進(jìn)制數(shù)據(jù)
name:接口的名字
這種方法內(nèi)部會(huì)將文件名當(dāng)做上傳到服務(wù)器之后的名字,并自動(dòng)獲取其類型
*/
[formData appendPartWithFormData:data name:@"file"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
// 上傳進(jìn)度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 上傳成功
NSLog(@"上傳成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 上傳失敗
NSLog(@"上傳失敗");
}];
方法二:formData直接添加url
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *url =@"http://120.25.226.186:32812/upload";
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
// formData 將要上傳的數(shù)據(jù)
// 直接傳URL
NSURL *url =[NSURL fileURLWithPath:@"/Users/yangboxing/Desktop/Snip20160905_7.png"];
// 方法一
[formData appendPartWithFileURL:url name:@"file" fileName:@"hhaha.png" mimeType:@"image/png" error:nil];
// 方法二
/**
這個(gè)方法會(huì)自動(dòng)截取url最后一塊的文件名作為上傳到服務(wù)器的文件名
也會(huì)自動(dòng)獲取mimeType,如果沒有辦法獲取mimeType 就使用@"application/octet-stream" 表示任意的二進(jìn)制數(shù)據(jù) ,當(dāng)我們不在意文件類型的時(shí)候 也可以用這個(gè)。
*/
[formData appendPartWithFileURL:url name:@"file" error:nil];
} progress:^(NSProgress * _Nonnull uploadProgress) {
// 上傳進(jìn)度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 上傳成功
NSLog(@"上傳成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 上傳失敗
NSLog(@"上傳失敗");
}];
注意:
mimeType表示文件的類型,關(guān)于mimeType類型可以自行百度,我們也可以通過(guò)發(fā)送請(qǐng)求獲取mineType
// 通過(guò)發(fā)送請(qǐng)求獲取mimeType
-(NSString *)connectSync:(NSString *)path
{
//1.確定請(qǐng)求路徑
NSURL *url = [NSURL fileURLWithPath:path];
//2.創(chuàng)建可變的請(qǐng)求對(duì)象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSHTTPURLResponse *res = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&res error:nil];
NSLog(@"%@",res.MIMEType);
return res.MIMEType;
}
我們通過(guò)[formData appendPartWithFileURL:url name:@"file" error:nil];來(lái)看看AFN是如何獲取mimeType的

進(jìn)入方法內(nèi)部

因此以后我們要獲取mimeType的時(shí)候也可以直接從AFN中復(fù)制拿去用嘍。
5. AFN網(wǎng)絡(luò)狀態(tài)的檢測(cè)
使用AFN進(jìn)行網(wǎng)絡(luò)狀態(tài)的檢測(cè)非常簡(jiǎn)單,并且可以持續(xù)監(jiān)聽網(wǎng)絡(luò)狀態(tài),每當(dāng)網(wǎng)絡(luò)狀態(tài)發(fā)生改變的時(shí)候,都會(huì)調(diào)用setReachabilityStatusChangeBlock方法
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
/*
AFNetworkReachabilityStatusUnknown = -1, 未知
AFNetworkReachabilityStatusNotReachable = 0, 沒有網(wǎng)絡(luò)
AFNetworkReachabilityStatusReachableViaWWAN = 1, 蜂窩流量
AFNetworkReachabilityStatusReachableViaWiFi = 2, 無(wú)線
*/
// 監(jiān)聽網(wǎng)絡(luò)狀態(tài)的變化
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"沒有網(wǎng)絡(luò)");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"3G");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"無(wú)線");
break;
default:
break;
}
}];
// 開啟
[manager startMonitoring];
6. AFN向HTTPS發(fā)送請(qǐng)求。
我們知道HTTPS在HTTP的基礎(chǔ)上加入了SSL協(xié)議,SSL依靠證書來(lái)驗(yàn)證服務(wù)器的身份,并為瀏覽器和服務(wù)器之間的通信加密。
當(dāng)使用NSURLSession來(lái)向HTTPS發(fā)送請(qǐng)求時(shí),需要在NSURLSessionDataDelegate的代理方法didReceiveChallenge中,信任服務(wù)器并且創(chuàng)建證書返回服務(wù)器。
而AFN對(duì)此進(jìn)行了很好的封裝,內(nèi)部已經(jīng)幫我們做好這些,因此向HTTPS發(fā)送請(qǐng)求方法與向HTTP發(fā)送請(qǐng)求是一樣的。
我們來(lái)看AFN內(nèi)部封裝的方法

那么當(dāng)我們用NSURLSession向HTTPS發(fā)送請(qǐng)求的時(shí)候,直接復(fù)制過(guò)來(lái)稍作修改就可以用啦
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
NSLog(@"--didReceiveChallenge--%@",challenge.protectionSpace);
/*
NSURLSessionAuthChallengeUseCredential = 0, 使用
NSURLSessionAuthChallengePerformDefaultHandling = 1, 忽略(默認(rèn))
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,忽略(會(huì)取消請(qǐng)求)
NSURLSessionAuthChallengeRejectProtectionSpace = 3, 忽略(下次繼續(xù)詢問)
*/
// NSURLAuthenticationMethodServerTrust 服務(wù)器信任
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//創(chuàng)建證書
NSURLCredential *credentoal = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credentoal);
}
}
三. 總結(jié)
我們一般在使用AFN的時(shí)候會(huì)將他封裝到一個(gè)工具類中,使工具類成為一個(gè)中間層,這樣便于我們使用和對(duì)代碼的管理,以后當(dāng)AFN更新或者我們要換網(wǎng)絡(luò)請(qǐng)求第三方類庫(kù)的時(shí)候,直接更改工具類就可以了,其他類中的的網(wǎng)絡(luò)請(qǐng)求方法都不用改變,這使我們以后維護(hù)代碼更加簡(jiǎn)單快捷方便。
關(guān)于網(wǎng)絡(luò)請(qǐng)求的基礎(chǔ)知識(shí)請(qǐng)參考iOS-網(wǎng)絡(luò)編程(一)HTTP協(xié)議、iOS-網(wǎng)絡(luò)編程(二)文件上傳和斷點(diǎn)離線下載。
?本文借鑒了很多前輩的文章,如果有不對(duì)的地方請(qǐng)指正,歡迎大家一起交流學(xué)習(xí) xx_cc 。