概况介绍:

这篇主要介绍AFNetworking中请求参数序列化的部分,具体代码在AFURLRequestSerialization中。AFURLRequestSerialization包含四部分:

  • AFHTTPRequestSerializaiton
  • AFJSONRequestSerializer
  • AFPropertyListRequestSerializer

AFHTTPRequestSerialization主要是设置http请求头,设置超时时间,BA认证,处理用户名密码登陆等等。主要功能分3大块:

  1. 处理所有的GET,HEAD,DELETE请求
  2. 处理content-type是application/x-www-form-urlencoded类型的POST请求
  3. 处理content-type是multipart/form-data类型的POST请求,请求的构建是通过AFStreamingMultipartFormData对象实现的。

AFJSONRequestSerializer继承自AFHTTPRequestSerialization类,使用NSJSONSerialization序列化json格式(application/json)的参数,将一个Dictionary对象转化成NSData,它只处理POST请求。

AFPropertyListRequestSerializer也继承了AFHTTPRequestSerialization类,使用NSPropertyListSerialization对象来序列化xml格式(application/x-plist)的参数,它也只处理POST请求。

综上所述,AFHTTPRequestSerialization是最重要也是最复杂的部分,源码也主要针对这部分做分析。

源码分析:

参数序列化和编码

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
71
72
73
74
75
76
77
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request

withParameters:(id)parameters

error:(NSError *__autoreleasing *)error

{

NSParameterAssert(request);

NSMutableURLRequest *mutableRequest = [request mutableCopy];
//设置http请求头
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id
value, BOOL * __unused stop) {

if (![request valueForHTTPHeaderField:field]) {

[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
//序列化参数
if (parameters) {

NSString *query = nil;
//queryStringSerilization是一个block,主要是用来自定义参数序列化的逻辑,返回一个序列化完成的结果
if (self.queryStringSerialization) {

NSError *serializationError;

query = self.queryStringSerialization(request, parameters, &
serializationError);

if (serializationError) {

if (error) {

*error = serializationError;

}

return nil;
}
} else {

switch (self.queryStringSerializationStyle) {
//使用AFNetworking默认的格式序列化,a=1&b=2这种
case AFHTTPRequestQueryStringDefaultStyle:
//这里parameters是一个dictionary对象,stringEncoding是给httpBody设置data的时候字符
//串的编码格式
query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding);

break;

}

}
//HTTPMethodsEncodingParametersInURI定义了GET,DELETE,HEAD3种方法
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request
HTTPMethod] uppercaseString]]) {

mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ?
@"&%@" : @"?%@", query]];

} else {
//POST
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {

[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

}
//setHTTPBody接受一个NSData的参数,将query转化成NSData
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
}

return mutableRequest;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//用于AFURLRequestSerialization内部调用的方法
static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {

NSMutableArray *mutablePairs = [NSMutableArray array];
//AFQueryStringPair是封装的键值对对象,主要是将dictionary中的键值对转化成query
//string形式,包括一些特殊字符的编码
//AFQueryStringPairsFromDictionary将dictionary转化成AFQueryStringPair集合
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {

[mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];

}
return [mutablePairs componentsJoinedByString:@"&"];

}
1
2
3
4
5
6
7
8
9
10
11
- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding {
//碰到nil或者NSNULL边界值的处理
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field
description], stringEncoding);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field
description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value
description], stringEncoding)];
}
}
1
2
3
4
5
6
7
8
9
//主要调用foundation函数CFURLCreateStringByAddingPercentEscapes,它主要是将querystri
//ng中的特殊字符(&,?)编码成“%+ASCII” 形式。根据文档,建议使用NSString
//stringByAddingPercentEncodingWithAllowedCharacters:]方法,这个方法使用UTF-8 encoding。
static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(
NSString *string, NSStringEncoding encoding) {
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge
CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString,
CFStringConvertNSStringEncodingToEncoding(encoding));
}

AFStreamingMutipartFormData

先看一个mutipart/form-data格式的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"

title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
  1. 它的Content-Type包含两部分,第一部分是multipart/form-data,第二部分是boundary, boundary一般是随机生成的,保持唯一即可。上面还需要有content-length,图里面没有。
  2. 每个参数的部分都有content-disposition,并且以–boundary开头,最后一个参数以–boundary–结尾。

AFNetworking通过AFStreamingMutipartFormData处理multipart/form-data格式的POST请求,它通过NSInputStream来构建http请求的body。每个参数的信息封装到了AFHTTPBodyPart中,所有的参数信息封装在AFMulipartBodyStream中。AFHTTPBodyPart中有NSInputStream对象用来将每个参数的写入到body,而AFMutipartBodyStream继承子NSInputStream处理所有参数的写入到body。其结构示意如下:

AFNetworkingArch

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
//处理multilpart/form-data(POST)请求
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method

URLString:(NSString *)URLString

parameters:(NSDictionary *)parameters

constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block

error:(NSError *__autoreleasing *)error

{
NSParameterAssert(method);

NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
//这里parameters传的是nil,因为当前格式(mutilpart/form-data)
//需要由AFStreamingMulitpartFormData去构建参数。
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method
URLString:URLString parameters:nil error:error];

__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest
stringEncoding:NSUTF8StringEncoding];

if (parameters) {

for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
//对于dictionary的参数,直接将它的值转成NSData处理,
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) {
//AFStreamingMultipartFormData对象的appendPartWithFormData将键值对转化成AFHTTPBodyP
//art并且放到AFMultipartBodyStream集合中。
[formData appendPartWithFormData:data name:[pair.field description]];

}

}

}

if (block) {
//当前block用来处理非dictionary的情况,比如参数可能是一个NSURL,或者直接就是一个NSInputStream。
block(formData);

}
//AFStreamingMultipartFormData的requestByFinalizingMultipartFormData主要是设置cont
//ent-Type和content-Length以及boundary
return [formData requestByFinalizingMultipartFormData];

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {

if ([self.bodyStream isEmpty]) {

return self.request;

}

// 设置boundary

[self.bodyStream setInitialAndFinalBoundaries];

//将AFStreamingMultipartFormData的AFMultipartBodyStream设置到http body stream上
[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;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)setInitialAndFinalBoundaries {

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

for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {

bodyPart.hasInitialBoundary = NO;

bodyPart.hasFinalBoundary = NO;

}
//设置boundary的头
[[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES];
//设置boundary的尾
[[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主要是设置每个参数部分的content-disposition
- (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];

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//构建AFHTTPBodyPart对象,这种情况下,AFHTTPBody对象的body都是nsdata类型
- (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];

}

除了NSData,还可以传入NSInputStream,NSURL去构建AFHTTPBodyPart对象,过程和NSData类似,设置Content-disposition和Content-Type,再通过data去构建AFHTTPBodyPart。

AFMultipartBodyStream

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
- (NSInteger)read:(uint8_t *)buffer

maxLength:(NSUInteger)length
{
//AFStreamingMutipartFormData将AFMultipartBodyStream设置到NSUrlRequest的httpbodys
//tream之后,foundation会自动调用read:maxLength:方法,改方法实现中遍历之前构建的所有AFHTTP
//BodyPart对象,分别调用它们的read:maxLength:方法来获取数据。


if ([self streamStatus] == NSStreamStatusClosed) {

return 0;

}

NSInteger totalNumberOfBytesRead = 0;

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wgnu"

while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.
numberOfBytesInPacket)) {

if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart
hasBytesAvailable]) {

if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator
nextObject])) {

break;

}

} else {

NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead;

NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&
buffer[totalNumberOfBytesRead] maxLength:maxLength];

if (numberOfBytesRead == -1) {

self.streamError = self.currentHTTPBodyPart.inputStream.
streamError;

break;

} else {

totalNumberOfBytesRead += numberOfBytesRead;

if (self.delay > 0.0f) {

[NSThread sleepForTimeInterval:self.delay];

}

}

}

}
return totalNumberOfBytesRead;

}

AFHTTPBodyPart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//根据body类型生成对应的inputStream
- (NSInputStream *)inputStream {

if (!_inputStream) {

if ([self.body isKindOfClass:[NSData class]]) {

_inputStream = [NSInputStream inputStreamWithData:self.body];

} else if ([self.body isKindOfClass:[NSURL class]]) {

_inputStream = [NSInputStream inputStreamWithURL:self.body];

} else if ([self.body isKindOfClass:[NSInputStream class]]) {

_inputStream = self.body;

} else {
_inputStream = [NSInputStream inputStreamWithData:[NSData data]];
}
}

return _inputStream;
}
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
- (NSInteger)read:(uint8_t *)buffer

maxLength:(NSUInteger)length

{

NSInteger totalNumberOfBytesRead = 0;
if (_phase == AFEncapsulationBoundaryPhase) {
//boundary部分,转成NSData
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ?
AFMultipartFormInitialBoundary(self.boundary) :
AFMultipartFormEncapsulationBoundary(self.boundary))
dataUsingEncoding:self.stringEncoding];

totalNumberOfBytesRead += [self readData:encapsulationBoundaryData
intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (
NSUInteger)totalNumberOfBytesRead)];

}
if (_phase == AFHeaderPhase) {
//content-disposition和content-length,转成NSData
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.
stringEncoding];

totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer
[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)
totalNumberOfBytesRead)];

}

if (_phase == AFBodyPhase) {
NSInteger numberOfBytesRead = 0;
//每个AFHTTPBodyPart的body部分,也就是实际传输的数据部分,都通过在inputStream方法里根据其实
//际数据类型转化成了NSInputStream类型对象,所以这里只需要调用foundation自带的read:maxLength方法就行了。
numberOfBytesRead = [self.inputStream read:&buffer[
totalNumberOfBytesRead] maxLength:(length - (NSUInteger)
totalNumberOfBytesRead)];

if (numberOfBytesRead == -1) {

return -1;

} else {

totalNumberOfBytesRead += numberOfBytesRead;
if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {

[self transitionToNextPhase];
}
}
}

if (_phase == AFFinalBoundaryPhase) {
//如果当前AFHTTPBodyPart是最后一个参数,那么会比其他参数多一个--boundary--的部分,转成NSData
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [
AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.
stringEncoding] : [NSData data]);

totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer
:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)
totalNumberOfBytesRead)];

}

return totalNumberOfBytesRead;

}
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
//将data读入buffer中
- (NSInteger)readData:(NSData *)data

intoBuffer:(uint8_t *)buffer

maxLength:(NSUInteger)length

{

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wgnu"
/*没有明白为什么读data的时候range为什么要从_phaseReadOffset开始,不应该是从0开始吗,因为每次
都是一个全新的data*/
NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length]
- ((NSUInteger)_phaseReadOffset), length));
//将data中range范围之内的数据复制到buffer里
[data getBytes:buffer range:range];

#pragma clang diagnostic pop

_phaseReadOffset += range.length;

if (((NSUInteger)_phaseReadOffset) >= [data length]) {

[self transitionToNextPhase];

}
return (NSInteger)range.length;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//序列化AFHTTPBodyPart的headers部分
- (NSString *)stringForHeaders {
NSMutableString *headerString = [NSMutableString string];

for (NSString *field in [self.headers allKeys]) {

[headerString appendString:[NSString stringWithFormat:@"%@: %@%@",
field, [self.headers valueForKey:field], kAFMultipartFormCRLF]];

}
[headerString appendString:kAFMultipartFormCRLF];
return [NSString stringWithString:headerString];

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//计算每个AFHTTPBodyPart的内容长度,包括传输参数,请求头和boundary信息,在AFMultiStream里会
//将所有的AFHTTPBodyPart的content-length加起来,做为整个POST请求体的content-length。
- (unsigned long long)contentLength {
unsigned long long length = 0;
//boundary
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ?
AFMultipartFormInitialBoundary(self.boundary) :
AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self
.stringEncoding];
length += [encapsulationBoundaryData length];
//headers
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.
stringEncoding];
length += [headersData length];
//data
length += _bodyContentLength;
//close boudary
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [
AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.
stringEncoding] : [NSData data]);
length += [closingBoundaryData length];
return length;

}