一. AFNetworking簡單介紹
通過前面學(xué)習(xí)的HTTP協(xié)議的基本知識聚霜,GET/POST請求的區(qū)別,NSURLConnection 和 NSURLSession的使用挫掏,已經(jīng)基本了解了網(wǎng)絡(luò)請求的方法炉媒,但是相對使用比較麻煩,AFNetworking是對NSURLConnection 和 NSURLSession的封裝铺韧,使網(wǎng)絡(luò)請求更加簡單輕松,是一款非常有用的第三方框架缓淹。AFNetworking3.0以后移除了對NSURLConnection的支持祟蚀。
看一下AFNetworking 歷史版本 以及框架
這些在GitHub上都可以看到,可以去GitHub上查看割卖。
二. AFNetworking使用
1. GET請求
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) {
//進度
//進度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// task 我們可以通過task拿到響應(yīng)頭
// responseObject:請求成功返回的響應(yīng)結(jié)果(AFN內(nèi)部已經(jīng)把響應(yīng)體轉(zhuǎn)換為OC對象,通常是字典或數(shù)組)
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// error 錯誤信息
}];
2. POST請求
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) {
// 進度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 請求成功
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 請求失敗
}];
注意:我們發(fā)現(xiàn)GET和POST請求一模一樣患雏,僅僅換了一個名字鹏溯,GET請求也可以將參數(shù)放在字典中,也可以將參數(shù)拼接在url之后parameters傳nil淹仑。另外不需要開啟Task丙挽,因為AFN內(nèi)部已經(jīng)幫我們開啟了
另外:AFN默認(rèn)會把服務(wù)器返回給我們的數(shù)據(jù)當(dāng)做是JSNO數(shù)據(jù)肺孵,并且AFN內(nèi)部已經(jīng)把響應(yīng)體JSON數(shù)據(jù)轉(zhuǎn)換為OC對象,通常是字典或數(shù)組颜阐。
那么如果服務(wù)器返回的XML呢平窘?這時我們需要自己設(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)更新過AFN之后這里會還原肤舞,出現(xiàn)問題比較難找)
3. 文件下載
// 1.創(chuàng)建一個管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2. 創(chuàng)建請求對象
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 臨時存儲地址
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 我們想要存儲的地址
// response 響應(yīng)頭
return url;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
// 下載完成之后調(diào)用
// response 響應(yīng)頭
// filePath 下載存儲地址
NSLog(@"filePath:%@",filePath);
}];
// 需要手動開啟
[downloadTask resume];
注意:
1. 下載文件需要獲取NSURLSessionDownloadTask對象手動開啟
2. 第一個block塊:downloadProgress 有兩個屬性completedUnitCount(已經(jīng)下載文件大凶闲隆)、totalUnitCount(文件總大欣钇省)芒率。
3. 第二個block塊:需要返回一個url,表示想要將文件存儲的地方篙顺。targetPath:表示臨時存儲地址在tmp臨時文件中偶芍。response:響應(yīng)頭 可以拿到一些文件信息
4. 第三個block塊:下載完成之后調(diào)用。response:響應(yīng)頭德玫。filePath:文件存儲地址匪蟀,與第二個block塊中返回的url是一個地址
4. 文件上傳
關(guān)于文件上傳使用AFN就簡單多了,也不需要我們?nèi)テ唇诱埱篌w和請求文件參數(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:上傳文件二進制數(shù)據(jù)
name:接口的名字
fileName:文件上傳到服務(wù)器之后叫什么名字
mineType:上傳文件的類型萄窜,可以上傳任意二進制mineType.
*/
[formData appendPartWithFileData:data name:@"file" fileName:@"123.png" mimeType:@"image/png"];
// 方法二
/**
data:上傳文件二進制數(shù)據(jù)
name:接口的名字
這種方法內(nèi)部會將文件名當(dāng)做上傳到服務(wù)器之后的名字,并自動獲取其類型
*/
[formData appendPartWithFormData:data name:@"file"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
// 上傳進度
} 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];
// 方法二
/**
這個方法會自動截取url最后一塊的文件名作為上傳到服務(wù)器的文件名
也會自動獲取mimeType撒桨,如果沒有辦法獲取mimeType 就使用@"application/octet-stream" 表示任意的二進制數(shù)據(jù) 查刻,當(dāng)我們不在意文件類型的時候 也可以用這個。
*/
[formData appendPartWithFileURL:url name:@"file" error:nil];
} progress:^(NSProgress * _Nonnull uploadProgress) {
// 上傳進度
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 上傳成功
NSLog(@"上傳成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 上傳失敗
NSLog(@"上傳失敗");
}];
注意:
mimeType表示文件的類型凤类,關(guān)于mimeType類型可以自行百度穗泵,我們也可以通過發(fā)送請求獲取mineType
// 通過發(fā)送請求獲取mimeType
-(NSString *)connectSync:(NSString *)path
{
//1.確定請求路徑
NSURL *url = [NSURL fileURLWithPath:path];
//2.創(chuàng)建可變的請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSHTTPURLResponse *res = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&res error:nil];
NSLog(@"%@",res.MIMEType);
return res.MIMEType;
}
我們通過[formData appendPartWithFileURL:url name:@"file" error:nil];
來看看AFN是如何獲取mimeType的
進入方法內(nèi)部
因此以后我們要獲取mimeType的時候也可以直接從AFN中復(fù)制拿去用嘍。
5. AFN網(wǎng)絡(luò)狀態(tài)的檢測
使用AFN進行網(wǎng)絡(luò)狀態(tài)的檢測非常簡單谜疤,并且可以持續(xù)監(jiān)聽網(wǎng)絡(luò)狀態(tài)佃延,每當(dāng)網(wǎng)絡(luò)狀態(tài)發(fā)生改變的時候,都會調(diào)用setReachabilityStatusChangeBlock方法
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
/*
AFNetworkReachabilityStatusUnknown = -1, 未知
AFNetworkReachabilityStatusNotReachable = 0, 沒有網(wǎng)絡(luò)
AFNetworkReachabilityStatusReachableViaWWAN = 1, 蜂窩流量
AFNetworkReachabilityStatusReachableViaWiFi = 2, 無線
*/
// 監(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(@"無線");
break;
default:
break;
}
}];
// 開啟
[manager startMonitoring];
6. AFN向HTTPS發(fā)送請求夷磕。
我們知道HTTPS在HTTP的基礎(chǔ)上加入了SSL協(xié)議履肃,SSL依靠證書來驗證服務(wù)器的身份,并為瀏覽器和服務(wù)器之間的通信加密坐桩。
當(dāng)使用NSURLSession來向HTTPS發(fā)送請求時尺棋,需要在NSURLSessionDataDelegate
的代理方法didReceiveChallenge
中,信任服務(wù)器并且創(chuàng)建證書返回服務(wù)器绵跷。
而AFN對此進行了很好的封裝膘螟,內(nèi)部已經(jīng)幫我們做好這些成福,因此向HTTPS發(fā)送請求方法與向HTTP發(fā)送請求是一樣的。
我們來看AFN內(nèi)部封裝的方法
那么當(dāng)我們用NSURLSession向HTTPS發(fā)送請求的時候荆残,直接復(fù)制過來稍作修改就可以用啦
-(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,忽略(會取消請求)
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的時候會將他封裝到一個工具類中奴艾,使工具類成為一個中間層,這樣便于我們使用和對代碼的管理内斯,以后當(dāng)AFN更新或者我們要換網(wǎng)絡(luò)請求第三方類庫的時候蕴潦,直接更改工具類就可以了,其他類中的的網(wǎng)絡(luò)請求方法都不用改變嘿期,這使我們以后維護代碼更加簡單快捷方便品擎。
關(guān)于網(wǎng)絡(luò)請求的基礎(chǔ)知識請參考iOS-網(wǎng)絡(luò)編程(一)HTTP協(xié)議、iOS-網(wǎng)絡(luò)編程(二)文件上傳和斷點離線下載备徐。
?本文借鑒了很多前輩的文章萄传,如果有不對的地方請指正,歡迎大家一起交流學(xué)習(xí) xx_cc 蜜猾。