iOS-网络编程(三)AFNetworking使用

一. AFNetworking简单介绍

通过前面学习的HTTP协议的基本知识,GET/POST请求的区别,NSURLConnection 和 NSURLSession的使用,已经基本了解了网络请求的方法,但是相对使用比较麻烦,AFNetworking是对NSURLConnection 和 NSURLSession的封装,使网络请求更加简单轻松,是一款非常有用的第三方框架。AFNetworking3.0以后移除了对NSURLConnection的支持。
看一下AFNetworking 历史版本 以及框架


AFNetworking版本更新
AFNetworking框架

这些在GitHub上都可以看到,可以去GitHub上查看。

二. AFNetworking使用

1. GET请求

    AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
    NSDictionary *dict = @{
        @"username":@"520it",
        @"pwd":@"520it"
        };
    // parameters 参数字典
    [manager GET:@"http://120.25.226.186:32812/login" parameters:dict progress:^(NSProgress * _Nonnull downloadProgress) {
        //进度
        //进度
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // task 我们可以通过task拿到响应头
        // responseObject:请求成功返回的响应结果(AFN内部已经把响应体转换为OC对象,通常是字典或数组)
    } 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) {
        // 请求失败
    }];

注意:我们发现GET和POST请求一模一样,仅仅换了一个名字,GET请求也可以将参数放在字典中,也可以将参数拼接在url之后parameters传nil。另外不需要开启Task,因为AFN内部已经帮我们开启了
另外:AFN默认会把服务器返回给我们的数据当做是JSNO数据,并且AFN内部已经把响应体JSON数据转换为OC对象,通常是字典或数组。
那么如果服务器返回的XML呢?这时我们需要自己设置AFN解析方式

manager.responseSerializer = [AFXMLParserResponseSerializer serializer];

返回的是NSXMLParser,需要我们自己解析

如果返回的数据既不是JSON也不是XML那么需要设置

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

AFN默认接收的ContentTypes 有以下三种

ContentType

如果服务器返回的ContentType不是这三种中的一种,我们就需要设置

manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];

也可以直接在AFN源码中添加(不建议使用,这种方式比较隐蔽,当更新过AFN之后这里会还原,出现问题比较难找)

直接添加即可

3. 文件下载

    // 1.创建一个管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 2. 创建请求对象
    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 当前下载大小
    // 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 响应头
        return url;
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        // 下载完成之后调用
        // response 响应头
        // filePath 下载存储地址
        NSLog(@"filePath:%@",filePath);
    }];
    // 需要手动开启
    [downloadTask resume];

注意:
1. 下载文件需要获取NSURLSessionDownloadTask对象手动开启
2. 第一个block块:downloadProgress 有两个属性completedUnitCount(已经下载文件大小)、totalUnitCount(文件总大小)。
3. 第二个block块:需要返回一个url,表示想要将文件存储的地方。targetPath:表示临时存储地址在tmp临时文件中。response:响应头 可以拿到一些文件信息
4. 第三个block块:下载完成之后调用。response:响应头。filePath:文件存储地址,与第二个block块中返回的url是一个地址

4. 文件上传

关于文件上传使用AFN就简单多了,也不需要我们去拼接请求体和请求文件参数啦,AFN内部已经帮我们拼接好了
方法一:formData 添加data形式数据

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    NSString *url =@"http://120.25.226.186:32812/upload";
    [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        // formData 将要上传的数据
        UIImage *image =[UIImage imageNamed:@"123"];
        NSData *data =UIImagePNGRepresentation(image);
        // 方法一
        /** 
          data:上传文件二进制数据
          name:接口的名字
          fileName:文件上传到服务器之后叫什么名字
          mineType:上传文件的类型,可以上传任意二进制mineType.
         */
        [formData appendPartWithFileData:data name:@"file" fileName:@"123.png" mimeType:@"image/png"];
        // 方法二
        /**
         data:上传文件二进制数据
         name:接口的名字
         这种方法内部会将文件名当做上传到服务器之后的名字,并自动获取其类型
         */
        [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 将要上传的数据
        // 直接传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最后一块的文件名作为上传到服务器的文件名 
         也会自动获取mimeType,如果没有办法获取mimeType 就使用@"application/octet-stream" 表示任意的二进制数据 ,当我们不在意文件类型的时候 也可以用这个。
         */
        [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表示文件的类型,关于mimeType类型可以自行百度,我们也可以通过发送请求获取mineType

// 通过发送请求获取mimeType
-(NSString *)connectSync:(NSString *)path
{
    //1.确定请求路径
    NSURL *url = [NSURL fileURLWithPath:path];
    //2.创建可变的请求对象
    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的

文件名称和mimeType获取

进入方法内部


mimeType获取

因此以后我们要获取mimeType的时候也可以直接从AFN中复制拿去用喽。

5. AFN网络状态的检测

使用AFN进行网络状态的检测非常简单,并且可以持续监听网络状态,每当网络状态发生改变的时候,都会调用setReachabilityStatusChangeBlock方法

    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    /*
    AFNetworkReachabilityStatusUnknown          = -1, 未知
    AFNetworkReachabilityStatusNotReachable     = 0,  没有网络
    AFNetworkReachabilityStatusReachableViaWWAN = 1,  蜂窝流量
    AFNetworkReachabilityStatusReachableViaWiFi = 2,  无线
    */
    // 监听网络状态的变化
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"3G");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"无线");
                break;
                
            default:
                break;
        }
    }];
    // 开启
    [manager startMonitoring];

6. AFN向HTTPS发送请求。

我们知道HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
当使用NSURLSession来向HTTPS发送请求时,需要在NSURLSessionDataDelegate的代理方法didReceiveChallenge中,信任服务器并且创建证书返回服务器。
而AFN对此进行了很好的封装,内部已经帮我们做好这些,因此向HTTPS发送请求方法与向HTTP发送请求是一样的。

我们来看AFN内部封装的方法

AFN内部封装的方法

那么当我们用NSURLSession向HTTPS发送请求的时候,直接复制过来稍作修改就可以用啦

-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    NSLog(@"--didReceiveChallenge--%@",challenge.protectionSpace);
    /*
     NSURLSessionAuthChallengeUseCredential = 0,      使用
     NSURLSessionAuthChallengePerformDefaultHandling = 1,   忽略(默认)
     NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,忽略(会取消请求)
     NSURLSessionAuthChallengeRejectProtectionSpace = 3, 忽略(下次继续询问)
     */
    // NSURLAuthenticationMethodServerTrust 服务器信任
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        //创建证书
        NSURLCredential *credentoal = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credentoal);
    }
}

三. 总结

我们一般在使用AFN的时候会将他封装到一个工具类中,使工具类成为一个中间层,这样便于我们使用和对代码的管理,以后当AFN更新或者我们要换网络请求第三方类库的时候,直接更改工具类就可以了,其他类中的的网络请求方法都不用改变,这使我们以后维护代码更加简单快捷方便。


关于网络请求的基础知识请参考iOS-网络编程(一)HTTP协议iOS-网络编程(二)文件上传和断点离线下载

✨本文借鉴了很多前辈的文章,如果有不对的地方请指正,欢迎大家一起交流学习 xx_cc 。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容