前言:
这篇文章主要讲解一下使用AFNetWorking(以下简称AFN)来自定义一个完整的网络请求类,来进行常用的网络请求后台数据的功能。 网上这样的例子很多,但是本文是基于AFNnetWorking的3.0以上版本的进行讲解的,同时也为支持ipv6协议(其实NSURLConnection也是支持ipv6的,博主模拟了ipv6运行环境,基于Connection的网络请求是没有任何问题的,也就是说AFN2.x以上,应该都没问题,这边提供一下 唐巧 的微信公众号放出来的文章:iOS应用支持 IPv6,就那点事儿),主要是针对NSURLConnection到NSURLSession的转变封装,至于网络请求的基类Session原理,后续文章会详细讲解。
下面直接看代码:
首先你的项目中应当已有AFN的第三方库的存在,可以使用pods来进行安装,至于还不会使用pods来管理第三方开源库的同学,请去自行谷歌,pods不在本篇的讨论范围之内。CocoaPods官网
首先我们新建一个继承自NSObject的自定义类,import引入AFN的头文件:
1 2 3 4
| import <Foundation/Foundation.h> import "AFNetworking.h @interface HGHttpRequestManager : NSObject
|
实例化session网络请求管理者:
@property (nonatomic,strong)AFHTTPSessionManager *httpRequestManager;
设置这个类的单例方法,以便在实例化类的时候使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| //BaseUrl,固定接口形式;yyy处填写不同模块需要拼接的请求短路径 const static NSString *BaseUrl = @"http://xxx.com/yyy";
static HGHttpRequestManager *manager = nil;
@implementation HGHttpRequestManager //生成类对象单例,这都是知道的了(不知道留评论,我手把手教你,哈哈) + (HGHttpRequestManager *)sharedManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!manager) { manager = [[super alloc] init]; } }); return manager; }
|
在封装请求方法之前,这里先定义一个方法,用来拼接每次发起请求所需要的参数集合,返回一个Dictionary,这个字典的可变参数是可配置的,每次请求都有必传参数,首先在这个集合中定义完成,包括:签名、系统版本os、userToken、version、deviceID;这些都是每次请求必传参数列表:
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
| //这个方法是封装一些基础参数,或者说每次请求API接口必传的参数,可根据自己的业务逻辑进行合理的封装 - (NSDictionary *)formatParameters:(NSMutableDictionary *)parameters{ NSMutableDictionary *dictAllParams = [NSMutableDictionary dictionaryWithCapacity:0]; //设置传入参数 if (parameters) { [dictAllParams addEntriesFromDictionary:parameters]; } [dictAllParams addEntriesFromDictionary:[self commonParameters]]; //设置token if ([HGUserData sharedInstance].userToken) { [dictAllParams setValue:[HGUserData sharedInstance].userToken forKey:kUDKey_UserToken]; } // 排序升序,签名value NSString *strSigned = [[HGSecurityManager sharedInstance] signParameters:dictAllParams withKey:HGX_KEY]; [dictAllParams setValue:strSigned forKey:@"sign"]; return dictAllParams.copy; } //生成字典,把结果return给上面的方法,以供GET或POST请求时获取 - (NSDictionary *)commonParameters{ NSMutableDictionary *dict = [[NSMutableDictionary alloc]init]; [dict safeSetValue:API_VER forKey:@"version"]; [dict safeSetValue:[OpenUDID value] forKey:@"deviceId"]; [dict safeSetValue:@"2" forKey:@"os"]; return dict.copy; }
|
封装GET方法:
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
| - (void)getRequestWithPamameters:(NSMutableDictionary *)parameters ToPath:(NSString *)path success:(void (^)(NSDictionary *result))success failure:(void (^)(NSError *error))failure { //拼接参数 NSDictionary *dictAllParams = [self formatParameters:parameters]; //请求路径 NSString *strUrl = [NSString stringWithFormat:@"%@%@",BaseUrl,path]; AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager]; //设定可以接收的返回类型 sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html",@"text/json",@"text/javascript", nil]; [sessionManager GET:strUrl parameters:dictAllParams progress:^(NSProgress * _Nonnull downloadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //成功或失败,用之前定义好的block把状态给回调出来 if (success) { HGBaseModel *baseModel = [[HGBaseModel alloc]initWithDictionary:responseObject error:nil]; //这里是一个code状态码,自己和后台约束设定的,code为99则表示用户还未登录,直接跳转到登录页面 if (baseModel.code == 99) { [[HGUserData sharedInstance] setIsLogin:@NO]; [self gotoLogin]; } success(responseObject); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { if (failure) { failure(error); } }]; }
|
封装POST方法:
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
| //参数列表使用封装的基本参数之后,会自动放到body之内 - (void)postRequestWithPamameters:(NSMutableDictionary *)parameters ToPath:(NSString *)path success:(void (^)(NSDictionary *result))success failure:(void (^)(NSError *error))failure { //拼接参数 NSDictionary *dictAllParams = [self formatParameters:parameters];
//请求路径 NSString *strUrl = [NSString stringWithFormat:@"%@%@",BaseUrl,path];
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager]; [sessionManager POST:strUrl parameters:dictAllParams progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { [self printSucceededResponseObject:responseObject withReqestUrl:strUrl andParams:dictAllParams]; if (success) { success(responseObject); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { [self printError:error withReqestUrl:strUrl andParams:dictAllParams]; if (failure) { failure(error); } }]; }
|
我们来看一下具体的使用方法,请求一个接口,返回一串json数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| //首先,直接生成单例对象,调用封装好的GET方法: //这里在请求时,加载一下loading转圈提示,提示用户在进行网络请求 [self showLoading]; [[HGHttpRequestManager sharedManager] getRequestWithPamameters:dictParams ToPath:kAPIPathOrderCancel success:^(NSDictionary *result) { //请求完成后,隐藏loading [self hideLoading]; //model中封装的一个init方法,直接把请求结果result的dictionary格式数据转化成model存储 OrderListModel *orderListModel = [[OrderListModel alloc]initWithDictionary:result error:nil]; if (orderListModel.isValid) {
[self showSucceededHud:orderListModel.msg]; //此时model有值,把model中的数组数据放到全局数组之中,这个数组就是数据源,然后调用自定义的一些方法,我这里是刷新订单列表OrderList [self.mArrayOrderList addObjectsFromArray:orderListModel.data]; [self getOrderList]; [self.listTableView reloadData]; }else{ [self showErrorHud:orderListModel.msg]; } } failure:^(NSError *error) { //请求失败也要隐藏loading [self hideLoading]; }];
|
这样一套完整的网络请求流程就完成了
这里有几点要说明下:
1、这里AFNetWorking使用的是3.1.0版本,3.0以下版本不适用于本文(如果需要,可以去看下源码,里面的请求类是RequestOperationManager)
2、Model封装方法就不展示了,model是基于JSONModel框架的,有兴趣的同学可以看下怎样继承,然后进行model解析
3、这里面的主要的一点是封装每次必传的请求参数,这是有些同学经常请求API写的很乱的一个原因所在。