AFNetworking3.0 网络请求工具类的简单封装

开发中,通常需要对网络请求进行一些封装处理以方便开发人员调用。当前,大多数iOS项目都采用Mattt Thompson主导的AFNetworking开源库处理网络请求。有些项目里直接使用AFNetworking的相关请求方法,把请求的实现直接暴露在外部。从程序设计的角度看,这是不合适的。请求实现分散在项目的各个角落,当需要同时修改请求的某个配置时,注定是场灾难。因而,我们一般会在AFNetworking等网络库的基础上,进行一层抽象,通过网络请求工具类包装接口,统一调度,并依据项目自身接口环境配置请求参数。以下模拟接口环境,对AFNetworking 3.0网络请求库进行一些简单的封装:
首先,创建网络请求工具类,并进行一些统一配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
@property (nonatomic, strong) AFHTTPSessionManager *manager;

+ (CHHTTPHelper *)defaultHTTPHelper
{
static CHHTTPHelper *instance = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});

return instance;
}

统一配置:

1
2
3
4
self.manager = [AFHTTPSessionManager manager];

self.manager.requestSerializer.timeoutInterval = 10; // 请求时长
self.manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects: @"text/plain", @"application/json", @"text/json", @"text/javascript", @"text/html", @"image/png", nil];

之后,根据接口环境,进行HOST路径的配置:
接口环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BASE_HOST:
内网:www.developer.com
外网:www.public.com

HOST:
内网:http://www.developer.com
外网:http://www.public.com

端口:
1020
2030

HOST_PATH:
内网:http://www.development.com:1020
外网:http://www.public.com:2030

完整请求路径:
内网:http://www.development.com:1020/module/api
外网:http://www.public.com:2030/module/api

考虑内外网等情况,配置HOST路径:
获取网络协议头:

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
typedef NS_ENUM(NSInteger, CHNetworkProtocolType) { 
CHNetworkProtocolTypeNone = 0,
CHNetworkProtocolTypeHTTP,
CHNetworkProtocolTypeHTTPS,
CHNetworkProtocolTypeFTP
};

+ (NSString *)networkProtocol:(CHNetworkProtocolType)protocolType
{
NSString *protocolString = @"";

if (protocolType == CHNetworkProtocolTypeNone)
{
return protocolString;
}
else if (protocolType == CHNetworkProtocolTypeHTTP)
{
protocolString = @"http";
}
else if (protocolType == CHNetworkProtocolTypeHTTPS)
{
protocolString = @"https";
}
else if (protocolType == CHNetworkProtocolTypeFTP)
{
protocolString = @"ftp";
}

return [protocolString stringByAppendingString:@"://"];
}

获取BASE_HOST:

1
2
3
4
5
6
7
8
9
10
11
12
static NSString * const BASE_HOST_DEVELOPER = @"www.developer.com"; 
static NSString * const BASE_HOST_PUPLIC = @"www.public.com";

+ (NSString *)baseHost
{
#ifdef DEBUG
return BASE_HOST_DEVELOPER; // change
// return BASE_HOST_PUPLIC; // change
#else
return BASE_HOST_PUPLIC;
#endif
}

获取端口号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef NS_ENUM(NSInteger, CHServerPortType) { 
CHServerPortTypeNone = 0,
CHServerPortType1020 = 1020,
CHServerPortType2030 = 2030,
};

+ (NSString *)port:(CHServerPortType)portType
{
NSString *portString = @"";

if (portType == CHServerPortTypeNone) return portString;

return [@":" stringByAppendingString:[NSString stringWithFormat:@"%ld", (long)portType]]; // :1020
}

根据网络协议头、BASE_PATH和端口号,获取路径:

1
2
3
4
5
6
7
8
+ (NSString *)hostPathURLWithNetworkProtocol:(CHNetworkProtocolType)protocolType baseHost:(NSString *)baseHost portType:(CHServerPortType)portType
{
NSString *URLString = [CHHTTPHelper networkProtocol:protocolType];

URLString = [URLString stringByAppendingString:baseHost];

return [URLString stringByAppendingString:[CHHTTPHelper port:portType]];
}

获取HOST:

1
2
3
4
5
6
7
8
9
10
11
+ (NSString *)host
{
static NSString *host = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
host = [CHHTTPHelper hostPathURLWithNetworkProtocol:CHNetworkProtocolTypeHTTP baseHost:[CHHTTPHelper baseHost] portType:CHServerPortTypeNone];
});

return host;
}

获取HOST_PATH:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+ (NSString *)hostPath
{
static NSString *hostPath = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([[CHHTTPHelper baseHost] isEqualToString:BASE_HOST_DEVELOPER])
{
hostPath = [CHHTTPHelper hostPathURLWithNetworkProtocol:CHNetworkProtocolTypeHTTP baseHost:[CHHTTPHelper baseHost] portType:CHServerPortType1020];
return;
}

hostPath = [CHHTTPHelper hostPathURLWithNetworkProtocol:CHNetworkProtocolTypeHTTP baseHost:[CHHTTPHelper baseHost] portType:CHServerPortType2030];
});

return hostPath;
}

HOST路径配置完成后,则可根据后台具体模块和API接口,设置完整请求路径:

1
2
3
4
5
6
7
8
9
static NSString * const MODULE_USER = @"user";
static NSString * const API_REGISTER_PATH = @"registered";

+ (NSString *)URLWithModule:(NSString *)module APIPath:(NSString *)APIPath
{
return [[[CHHTTPHelper hostPath] stringByAppendingPathComponent:module] stringByAppendingPathComponent:APIPath];
}

[CHHTTPHelper URLWithModule:MODULE_USER APIPath:API_REGISTER_PATH];

之后,则是处理请求结果。AFNetworking的请求回调内包含了成功回调和失败回调,但是,这里的成功和失败是根据网络连接失败或数据解析失败等硬请求失败来区分的。有些时候,项目本身业务要求,根据后台返回的一些错误字段提示,将请求结果归为失败请求,也应当进入失败回调处理。同时,有时候业务需求,我们可能也希望提供一个无论成功或失败回调后,都会进入的缓冲回调(所有完成回调)内。基于以上的业务考虑,我们应当对AFNetworking的请求回调再进行一层抽象处理:
假定后台返回如下字段要求区分请求失败或成功:

1
2
3
4
5
6
7
8
9
10
11
12
请求成功:
{
"result":1,
...
}

请求失败:
{
"result":0,
"exCode":"100",
"exMsg":"失败"
}

设置请求回调:

1
2
3
typedef void(^CHHTTPRequestSuccess)(NSURLSessionDataTask *task, id responseObject);
typedef void(^CHHTTPRequestFailure)(NSURLSessionDataTask *task, NSError *error, NSString *exCode, NSString *exMsg);
typedef void(^CHHTTPRequestAllCompletion)();

请求失败回调处理:
默认请求失败提示:

1
2
static NSString * const DEFAULT_EX_CODE = @"请求失败";
static NSString * const DEFAULT_EX_MSG = @"加载失败";

配置请求失败回调:

1
2
3
4
+ (void)configureRequestFailure:(CHHTTPRequestFailure)failure withTask:(NSURLSessionDataTask *)task error:(NSError *)error exCode:(NSString *)exCode exMsg:(NSString *)exMsg
{
!failure?:failure(task, error, exCode, exMsg);
}

根据返回数据, 配置请求失败回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+ (void)configureRequestFailure:(CHHTTPRequestFailure)failure withTask:(NSURLSessionDataTask *)task error:(NSError *)error responseObject:(id)responseObject
{
if (!responseObject)
{
[CHHTTPHelper configureRequestFailure:failure withTask:task error:error exCode:DEFAULT_EX_CODE exMsg:DEFAULT_EX_MSG];
return;
}

NSString *exCode = !responseObject[@"exCode"]?DEFAULT_EX_CODE:responseObject[@"exCode"];
NSString *exMsg = !responseObject[@"exMsg"]?DEFAULT_EX_MSG:responseObject[@"exMsg"];

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error exCode:exCode exMsg:exMsg];
}

+ (void)configureRequestFailure:(CHHTTPRequestFailure)failure withTask:(NSURLSessionDataTask *)task responseObject:(id)responseObject
{
[CHHTTPHelper configureRequestFailure:failure withTask:task error:nil responseObject:responseObject];
}

配置请求失败(硬请求失败)回调:

1
2
3
4
+ (void)configureRequestFailure:(CHHTTPRequestFailure)failure withTask:(NSURLSessionDataTask *)task error:(NSError *)error
{
[CHHTTPHelper configureRequestFailure:failure withTask:task error:error responseObject:nil];
}

请求成功回调处理:
配置请求成功回调:

1
2
3
4
+ (void)configureRequestSuccess:(CHHTTPRequestSuccess)success withTask:(NSURLSessionDataTask *)task responseObject:(id)responseObject
{
!success?:success(task, responseObject);
}

配置请求成功回调(处理result为0情况):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (void)configureRequestSuccess:(CHHTTPRequestSuccess)success requestFailure:(CHHTTPRequestFailure)failure withTask:(NSURLSessionDataTask *)task responseObject:(id)responseObject
{
if ([responseObject isKindOfClass:[NSDictionary class]])
{
if ([responseObject[@"result"] intValue] == 1)
{
[CHHTTPHelper configureRequestSuccess:success withTask:task responseObject:responseObject];
return;
}

[CHHTTPHelper configureRequestFailure:failure withTask:task responseObject:responseObject];
return;
}
}

所有完成回调(缓冲回调)处理:
配置所有完成回调(缓冲回调):

1
2
3
4
+ (void)configureRequestAllCompletion:(CHHTTPRequestAllCompletion)completion
{
!completion?:completion();
}

此后,基于以上的回调处理,对GET/HEAD/POST/PUT/PATCH/DELETE等请求方法进行封装处理即可:
GET请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager GET:URLString parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:responseObject];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

HEAD请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager HEAD:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:nil];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

POST请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager POST:URLString parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {

} progress:^(NSProgress * _Nonnull uploadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:responseObject];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

PUT请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (NSURLSessionDataTask *)PUT:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:responseObject];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

PATCH请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager PATCH:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:responseObject];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

DELETE请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString parameters:(NSDictionary *)parameters success:(CHHTTPRequestSuccess)success failure:(CHHTTPRequestFailure)failure allCompletion:(CHHTTPRequestAllCompletion)allCompletion
{
return [self.manager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

[CHHTTPHelper configureRequestSuccess:success requestFailure:failure withTask:task responseObject:responseObject];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

[CHHTTPHelper configureRequestFailure:failure withTask:task error:error];

[CHHTTPHelper configureRequestAllCompletion:allCompletion];
}];
}

至此,网络请求工具类基本部分完成,至于一些工具方法(检测网络连接状态等),可随需求添加。我们在需要请求的位置调用即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NSString *URLString = [CHHTTPHelper URLWithModule:MODULE_USER APIPath:API_REGISTER_PATH];

NSDictionary *parameters = @{...};

[[CHHTTPHelper defaultHTTPHelper] GET:URLString parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {

// do someting.

} failure:^(NSURLSessionDataTask *task, NSError *error, NSString *exCode, NSString *exMsg) {

// do someting.

} allCompletion:^{

// do someting.
}];

在实际开发里,我们通常还会在网络层上再抽象一层业务层,将具体的请求实现、数据转换及一些业务逻辑进行整合,方便外部调度以及后续维护。当然,这是另一个话题了。

附,示例代码:https://github.com/ColinHwang/CHHTTPHelper