AFNetWorking 源码学习笔记 ☞ Serialization

AFNetWorking 源码学习笔记.png

前言

本文是 AFNetWorking 源码学习笔记的第四篇,将介绍第三部分 – Serialization 目录下的文件:AFURLRequestSerialization(.h/.m) 和 AFURLResponseSerialization(.h/.m) ,仔细观察这些文件发现,AFURLRequestSerialization 和 AFURLResponseSerialization 不仅是文件名,还是 2 个协议的名称。

AFNetWorking-Serialization.png

正文

AFURLRequestSerialization(.h/.m)

先看 AFURLRequestSerialization.h 文件,这个文件里边共有 2 个协议和 3 个类,他们之间的关系如下:

AFURLRequestSerialization-Simple.png

这里,我们以 上一篇 提到的构造 request 的 2 个方法为主线开始介绍。

1
2
3
4
5
6
7
8
9
10
11
12
// 方法一

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method
URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
parameters:parameters
error:&serializationError];
// 方法二
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST"
URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
parameters:parameters
constructingBodyWithBlock:block
error:&serializationError];

先来看看第一类请求方法的实现中构建 MutableURLRequest 用到的方法:

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
33
34
35
36
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 0.检测 method、URLString 是否为空
NSParameterAssert(method);
NSParameterAssert(URLString);

NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);

// 1.开始创建(系统方法创建)
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

// 2.设置参数
// 2.1 请求方法
mutableRequest.HTTPMethod = method; // 方法默认是 “GET”

// 2.2 为 request 设置其一些默认参数
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {

// AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6个属性对应 getter 方法名 string 组成的数组
// self.mutableObservedChangedKeyPaths 中是 值非nil 的属性名
// requstSerializer 初始化时,创建了 self.mutableObservedChangedKeyPaths 这个空 mutableSet,然后,如果设置了对应的参数,它里边就会加上对应的属性名

if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}

// 3.对参数进行序列化,并赋值给 request
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

return mutableRequest;
}

该方法来自 AFHTTPRequestSerializer,具体功能见上边的代码注释,简单看一下部分细节,2.2 for 循环的代码块,其中 AFHTTPRequestSerializerObservedKeyPaths() 是一个静态函数,实际返回了当前类 AFHTTPRequestSerializer 中的几个重要的属性名对应的字符串数组,也可以认为是其 get 方法名对应的字符串数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)),
NSStringFromSelector(@selector(cachePolicy)),
NSStringFromSelector(@selector(HTTPShouldHandleCookies)),
NSStringFromSelector(@selector(HTTPShouldUsePipelining)),
NSStringFromSelector(@selector(networkServiceType)),
NSStringFromSelector(@selector(timeoutInterval))];
});

return _AFHTTPRequestSerializerObservedKeyPaths;
}

类的初始化方法里边主要设置了一些 header,然后就是给上边方法提到的几个属性添加了 KVO 监听,当用户给这些属性赋值 (可以为nil)时,就会给 self.mutableObservedChangedKeyPaths 这个 mutableSet 增删元素 (相关代码如下):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. init 方法中给属性添加监听的代码
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}

// 2. 触发的监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}

回到 for 循环中的语句,里边 if 语句的目的是将用户设置的属性值赋给 mutableRequest。

然后,来到 3. 序列化参数的时候,用到了这样一个方法 requestBySerializingRequest: withParameters: error:,它属于 AFURLRequestSerialization 这个协议,AFURLRequestSerializer 遵守协议并实现了该方法,注释见下方法注释。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#pragma mark - AFURLRequestSerialization

// 为 request 设置参数
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);

// 0.将 request 转成 mutable,以便修改
NSMutableURLRequest *mutableRequest = [request mutableCopy];

// 1.设置 header 字段,self.HTTPRequestHeaders 是在 init 方法中设置的 useragent 等参数
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
// 将 self.HTTPRequestHeaders 中的 header 参数设置到 request 里边
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];

// 2.参数序列化
NSString *query = nil;
if (parameters) {
// 2.1 self.queryStringSerialization 是一个 block,如果从外边 设置了序列化规则
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);

if (serializationError) {
if (error) {
*error = serializationError;
}

return nil;
}
} else {
// 2.2 使用AF提供的序列化规则,将 string、array、dictionary 的 parameters 统一转化成了 key1=value1&key2=value2&key3=value3 格式的字符串
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}

// 3.设置 request 的 URL 或 HTTPBody 👇

// 初始化时已经设置了 self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {

if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}

} else { // POST 等其他方法,将 query 设置成 body

// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
// 如果设置了 body,需要有 Content-Type
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
// self.stringEncoding 编码方式
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}

return mutableRequest;
}

至此,第一种创建 mutableRequest 的主要方法就介绍完了,在讨论第二种创建 MutableURLRequest 的方法实现之前,我们先看看 AFNetWorking 中给出的一个上传数据的示例:

1
2
3
4
5
6
7
8
9
10
11
12
// AFNetWorking 中提供的示例
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST"
URLString:@"http://example.com/upload"
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
{
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"]
name:@"file"
fileName:@"filename.jpg"
mimeType:@"image/jpeg"
error:nil];
} error:nil];

关注一下 constructingBodyWithBlock 中的代码就行,其中调用了参数 formData 遵守的一个协议方法 appendPartWithFileURL: ...。下面开始查看创建 MutableURLRequest 的方法实现:

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
33
34
35
36
37
38
39
40
41
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError *__autoreleasing *)error
{
// 0.过滤不符合要求的情况
NSParameterAssert(method);
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

// 1.创建 mutableRequest
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

// 2.构建 formData
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

if (parameters) {
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
NSData *data = nil;
if ([pair.value isKindOfClass:[NSData class]]) {
data = pair.value;
} else if ([pair.value isEqual:[NSNull null]]) {
data = [NSData data];
} else {
data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
}

if (data) {
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}

// 3.执行外部传入的 block
if (block) {
block(formData);
}

// 4.返回最终处理完的 request
return [formData requestByFinalizingMultipartFormData];
}

大概内容见上方代码注释,下面我们讨论一下细节部分。

1.创建 mutableRequest 的方法实现及注释如下:

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
33
34
35
36
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 0.检测 method、URLString 是否为空
NSParameterAssert(method);
NSParameterAssert(URLString);

NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);

// 1.开始创建(系统方法创建)
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

// 2.设置参数
// 2.1 请求方法
mutableRequest.HTTPMethod = method; // 方法默认是 “GET”

// 2.2 为 request 设置其一些默认参数
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {

// AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6个属性对应 getter 方法名 string 组成的数组
// self.mutableObservedChangedKeyPaths 中是 值非nil 的属性名
// requstSerializer 初始化时,创建了 self.mutableObservedChangedKeyPaths 这个空 mutableSet,然后,如果设置了对应的参数,它里边就会加上对应的属性名

if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}

// 3.对参数进行序列化,与第一种方法调用的是相同的方法,不过多介绍
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

return mutableRequest;
}

2.构建 AFStreamingMultipartFormData * 类型的 formData,并为其添加数据。下面是它的几个重要方法:

AFStreamingMultipartFormData 是一个用于给 request 设置数据的类。通过初始化方法 initWithURLRequest: stringEncoding: ,将 request 赋值给 AFStreamingMultipartFormData 内部的 copy 属性 request,用于后边的处理及最终返回,。

formData 遵守协议 AFMultipartFormData,当然要实现其中的一些协议方法了,几个重要的方法 (协议方法和非协议方法) 实现见下方代码片段。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// AFStreamingMultipartFormData : NSObject <AFMultipartFormData>

- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
stringEncoding:(NSStringEncoding)encoding
{
self = [super init];
if (!self) {
return nil;
}

self.request = urlRequest;
self.stringEncoding = encoding;
self.boundary = AFCreateMultipartFormBoundary();
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];

return self;
}

- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name
{
NSParameterAssert(name);

NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];

[self appendPartWithHeaders:mutableHeaders body:data];
}

- (void)appendPartWithHeaders:(NSDictionary *)headers
body:(NSData *)body
{
NSParameterAssert(body);

AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = headers;
bodyPart.boundary = self.boundary;
bodyPart.bodyContentLength = [body length];
bodyPart.body = body;

[self.bodyStream appendHTTPBodyPart:bodyPart];
}

- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
if ([self.bodyStream isEmpty]) {
return self.request;
}

// Reset the initial and final boundaries to ensure correct Content-Length
[self.bodyStream setInitialAndFinalBoundaries];
[self.request setHTTPBodyStream:self.bodyStream];

[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];

return self.request;
}

我们发现,其中用到了 AFMultipartBodyStream 和 AFHTTPBodyPart 这 2 个类,其中 AFMultipartBodyStream 继承自 NSInputStream,将会作为参数传给 request。而 AFHTTPBodyPart 用于构造分段数据,并传递给 AFMultipartBodyStream。下边是 2 者的部分方法实现:

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
33
34
// AFMultipartBodyStream : NSInputStream <NSStreamDelegate>

@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;

// 添加 part
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
[self.HTTPBodyParts addObject:bodyPart];
}

// 对所有的 part 组成的数组进行处理:第一个 part 的 hasInitialBoundary = YES,最后一个 part 的 hasFinalBoundary = YES,其他 part 的这两个属性s都是 NO。
// 这个在计算分段大小的时候会用到
- (void)setInitialAndFinalBoundaries {

if ([self.HTTPBodyParts count] > 0) {

for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
bodyPart.hasInitialBoundary = NO;
bodyPart.hasFinalBoundary = NO;
}

[[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
[[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
}
}

// 计算所有分段加起来的总数据
- (unsigned long long)contentLength {
unsigned long long length = 0;
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
length += [bodyPart contentLength];
}

return length;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// AFHTTPBodyPart : NSObject

// 计算每一个分段的大小
- (unsigned long long)contentLength {
unsigned long long length = 0;

// hasInitialBoundary 是前边 AFMultipartBodyStream 里的 setInitialAndFinalBoundaries 方法里边设置过的哦
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
length += [encapsulationBoundaryData length];

NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
length += [headersData length];

length += _bodyContentLength;

// hasFinalBoundary 也是前边设置过的
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
length += [closingBoundaryData length];

return length;
}

3.执行 block ,其实也是在给 formData 添加数据,block 的实现中必须调用 AFMultipartFormData 中的协议方法。

4.返回结果时,调用了 formData 自己的方法 requestByFinalizingMultipartFormData(非协议方法),即为 request 的相关字段赋值,并将其返回。

最后,我们以这些类之间的关系图最为本部分的小结:

AFURLRequestSerialization.png

AFURLResponseSerialization

如前文所述,AFURLResponseSerialization 即是协议名称,又是文件名。之前在讨论 NSURLSessionTaskDelegate 的代理方法 - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error; 时有这样一行代码:

1
2
3
4
5
6
7
// AFURLSessionManagerTaskDelegate(Class)

// *** 使用 responseSerializer 处理返回结果
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

// AFURLSessionManager(Class)
self.responseSerializer = [AFJSONResponseSerializer serializer];

我们知道,这里的 manager 指的是 AFURLSessionManager,在其 init() 方法中初始化了一个重要属性 responseSerializer。那么 AFURLResponseSerialization 这个文件里边到底有哪些类,他们之间的关系又是怎样的?

AFURLResponseSerialization.png

从上图可以发现,这里有一个重要的基类 AFHTTPResponseSerializer,他遵守 AFURLResponseSerialization 这个协议 (只有一个协议方法:- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error;),其它几个类都是他的子类,他们都各自实现了该协议方法 ,用来处理接口返回的数据。

下面我们从基类 AFHTTPResponseSerializer 开始讨论,下面是它的头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>

// 编码方式,不过源码中已经指明 It is never used。
@property (nonatomic, assign) NSStringEncoding stringEncoding;
// 可接受的 HTTP 状态码
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
// 可接受的 MIME types
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;


// 创建及初始化方法,创建默认配置的 serializer
- (instancetype)init;
+ (instancetype)serializer;

// 校验接口返回的指定 response 和 data,默认检查了 acceptable status code 和 content type
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;

@end

上边 3 个属性中的第 1 个 stringEncoding 已经被标记为过期,即不再对接收到的数据进行解码 (decode)。acceptableStatusCodes 和 acceptableContentTypes 是两个集合(set),依次表示客户端可以接受的响应报文的 HTTP 状态码和 Content-Type,用于后边对响应报文 response 和 data 进行校验。

再来看创建及初始化方法的实现,实际重点在 init 方法中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+ (instancetype)serializer {
return [[self alloc] init];
}

- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}

self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;

return self;
}

即给 acceptableStatusCodes 和 acceptableContentTypes 赋初值,前者默认为从 200 到 299 之间的整数,及成功的状态码,后者默认为 nil,也就是说接收任何类型 (Content-Type) 的响应数据。

现在来看开头提的解析数据的方法:- (id)responseObjectForResponse: data: error:

1
2
3
4
5
6
7
8
9
10
#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

return data;
}

这其实是 AFURLResponseSerialization 规定的协议方法,首先校验了返回的 response 和 data,如果出错,就将错误信息写入 error。最后将 data 返回。用于校验的方法实现如下:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;

// 1.response 是 HTTP 响应报文的情况
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {

// 2.ContentType 不符的情况
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {

if ([data length] > 0 && [response URL]) {

NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}

validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}

responseIsValid = NO;
}

// 2.StatusCode 不符的情况
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];

if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}

validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);

responseIsValid = NO;
}
}

// 3.保存错误信息-有错误信息并且response不符合要求的情况
if (error && !responseIsValid) {
*error = validationError;
}

// 4.返回
return responseIsValid;
}

代码看起来有点长,实际做的事情比较少,首先判断响应的报文是否是 HTTP 的响应报文,如果不是,直接返回 responseIsValid (此时为 YES),如果是 HTTP 报文时才会进行接下来的判断。接着分别判断 StatusCode 和 ContentType 是否符合要求,如果不符合要求,则构建相应的 validationError 信息,并将 responseIsValid 置为 NO。

当 error 存在,并且 responseIsValid 为 NO 时,将 validationError 赋值给 error。最后返回校验结果。

至于 AFHTTPResponseSerializer 的子类,我们就只看一下 AFJSONResponseSerializer,其他子类与之类似,就不多做介绍了。

AFJSONResponseSerializer 是专门用于解析 JSON 格式的响应数据的。他重写了父类的初始化方法,给 acceptableContentTypes 添加了 application/json、text/json、text/javascript 这 3 中 json 相关的格式。

1
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

同时通过两个工厂方法为属性 readingOptions 赋初值:

1
2
3
4
5
6
7
8
9
10
+ (instancetype)serializer {
return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
}

+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
AFJSONResponseSerializer *serializer = [[self alloc] init];
serializer.readingOptions = readingOptions;

return serializer;
}

最后,重写了解析数据的方法 (如下),先是和父类一样校验了 ContentType 和 StatusCode,然后反序列化拿到的响应数据,过滤掉反序列化后的数据中的 value 为 Null 的数据,最后将处理完的数据返回。

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
33
34
35
36
37
38
39
40
#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 1.调用父类方法,校验 ContentType 和 statusCode
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}

// 异常处理
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) {
return nil;
}

NSError *serializationError = nil;

// 2.反序列化数据
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}

// 3.移除 value 为 Null 的 键值对
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}

return responseObject;
}

以上就是构建及序列化请求参数的 AFHTTPRequestSerializer 和 处理响应数据的 AFHTTPResponseSerializer 的主要实现代码,当然还有很多细节,在此就不再多做介绍了。

参考

https://blog.csdn.net/tsunamier/article/details/53611811
https://blog.csdn.net/tsunamier/article/details/53673751

0%