概況介紹:
這篇主要介紹AFNetworking中請(qǐng)求參數(shù)序列化的部分,具體代碼在AFURLRequestSerialization中。AFURLRequestSerialization包含四部分:
- AFHTTPRequestSerializaiton
- AFJSONRequestSerializer
- AFPropertyListRequestSerializer
AFHTTPRequestSerialization主要是設(shè)置http請(qǐng)求頭,設(shè)置超時(shí)時(shí)間,BA認(rèn)證,處理用戶名密碼登陸等等。主要功能分3大塊:
- 處理所有的GET,HEAD,DELETE請(qǐng)求
- 處理content-type是application/x-www-form-urlencoded類型的POST請(qǐng)求
- 處理content-type是multipart/form-data類型的POST請(qǐng)求,請(qǐng)求的構(gòu)建是通過(guò)AFStreamingMultipartFormData對(duì)象實(shí)現(xiàn)的。
AFJSONRequestSerializer繼承自AFHTTPRequestSerialization類,使用NSJSONSerialization序列化json格式(application/json)的參數(shù),將一個(gè)Dictionary對(duì)象轉(zhuǎn)化成NSData,它只處理POST請(qǐng)求。
AFPropertyListRequestSerializer也繼承了AFHTTPRequestSerialization類,使用NSPropertyListSerialization對(duì)象來(lái)序列化xml格式(application/x-plist)的參數(shù),它也只處理POST請(qǐng)求。
綜上所述,AFHTTPRequestSerialization是最重要也是最復(fù)雜的部分,源碼也主要針對(duì)這部分做分析。
源碼分析:
參數(shù)序列化和編碼
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//設(shè)置http請(qǐng)求頭
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id
value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
//序列化參數(shù)
if (parameters) {
NSString *query = nil;
//queryStringSerilization是一個(gè)block,主要是用來(lái)自定義參數(shù)序列化的邏輯,返回一個(gè)序列化完成的結(jié)果
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &
serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
//使用AFNetworking默認(rèn)的格式序列化,a=1&b=2這種
case AFHTTPRequestQueryStringDefaultStyle:
//這里parameters是一個(gè)dictionary對(duì)象,stringEncoding是給httpBody設(shè)置data的時(shí)候字符
//串的編碼格式
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接受一個(gè)NSData的參數(shù),將query轉(zhuǎn)化成NSData
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
}
return mutableRequest;
}
//用于AFURLRequestSerialization內(nèi)部調(diào)用的方法
static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {
NSMutableArray *mutablePairs = [NSMutableArray array];
//AFQueryStringPair是封裝的鍵值對(duì)對(duì)象,主要是將dictionary中的鍵值對(duì)轉(zhuǎn)化成query
//string形式,包括一些特殊字符的編碼
//AFQueryStringPairsFromDictionary將dictionary轉(zhuǎn)化成AFQueryStringPair集合
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
- (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)];
}
}
//主要調(diào)用foundation函數(shù)CFURLCreateStringByAddingPercentEscapes,它主要是將querystri
//ng中的特殊字符(&,?)編碼成“%+ASCII” 形式。根據(jù)文檔,建議使用NSString
//stringByAddingPercentEncodingWithAllowedCharacters:]方法,這個(gè)方法使用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
先看一個(gè)mutipart/form-data格式的請(qǐng)求
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--
- 它的Content-Type包含兩部分,第一部分是multipart/form-data,第二部分是boundary, boundary一般是隨機(jī)生成的,保持唯一即可。上面還需要有content-length,圖里面沒(méi)有。
- 每個(gè)參數(shù)的部分都有content-disposition,并且以--boundary開(kāi)頭,最后一個(gè)參數(shù)以--boundary--結(jié)尾。
AFNetworking通過(guò)AFStreamingMutipartFormData處理multipart/form-data格式的POST請(qǐng)求,它通過(guò)NSInputStream來(lái)構(gòu)建http請(qǐng)求的body。每個(gè)參數(shù)的信息封裝到了AFHTTPBodyPart中,所有的參數(shù)信息封裝在AFMulipartBodyStream中。AFHTTPBodyPart中有NSInputStream對(duì)象用來(lái)將每個(gè)參數(shù)的寫入到body,而AFMutipartBodyStream繼承子NSInputStream處理所有參數(shù)的寫入到body。其結(jié)構(gòu)示意如下:
//處理multilpart/form-data(POST)請(qǐng)求
- (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,因?yàn)楫?dāng)前格式(mutilpart/form-data)
//需要由AFStreamingMulitpartFormData去構(gòu)建參數(shù)。
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)) {
//對(duì)于dictionary的參數(shù),直接將它的值轉(zhuǎn)成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對(duì)象的appendPartWithFormData將鍵值對(duì)轉(zhuǎn)化成AFHTTPBodyP
//art并且放到AFMultipartBodyStream集合中。
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}
if (block) {
//當(dāng)前block用來(lái)處理非dictionary的情況,比如參數(shù)可能是一個(gè)NSURL,或者直接就是一個(gè)NSInputStream。
block(formData);
}
//AFStreamingMultipartFormData的requestByFinalizingMultipartFormData主要是設(shè)置cont
//ent-Type和content-Length以及boundary
return [formData requestByFinalizingMultipartFormData];
}
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
if ([self.bodyStream isEmpty]) {
return self.request;
}
// 設(shè)置boundary
[self.bodyStream setInitialAndFinalBoundaries];
//將AFStreamingMultipartFormData的AFMultipartBodyStream設(shè)置到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;
}
- (void)setInitialAndFinalBoundaries {
if ([self.HTTPBodyParts count] > 0) {
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
bodyPart.hasInitialBoundary = NO;
bodyPart.hasFinalBoundary = NO;
}
//設(shè)置boundary的頭
[[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES];
//設(shè)置boundary的尾
[[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
}
}
//主要是設(shè)置每個(gè)參數(shù)部分的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];
}
//構(gòu)建AFHTTPBodyPart對(duì)象,這種情況下,AFHTTPBody對(duì)象的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去構(gòu)建AFHTTPBodyPart對(duì)象,過(guò)程和NSData類似,設(shè)置Content-disposition和Content-Type,再通過(guò)data去構(gòu)建AFHTTPBodyPart。
AFMultipartBodyStream
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
//AFStreamingMutipartFormData將AFMultipartBodyStream設(shè)置到NSUrlRequest的httpbodys
//tream之后,foundation會(huì)自動(dòng)調(diào)用read:maxLength:方法,改方法實(shí)現(xiàn)中遍歷之前構(gòu)建的所有AFHTTP
//BodyPart對(duì)象,分別調(diào)用它們的read:maxLength:方法來(lái)獲取數(shù)據(jù)。
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
//根據(jù)body類型生成對(duì)應(yīng)的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;
}
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
NSInteger totalNumberOfBytesRead = 0;
if (_phase == AFEncapsulationBoundaryPhase) {
//boundary部分,轉(zhuǎn)成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,轉(zhuǎn)成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;
//每個(gè)AFHTTPBodyPart的body部分,也就是實(shí)際傳輸?shù)臄?shù)據(jù)部分,都通過(guò)在inputStream方法里根據(jù)其實(shí)
//際數(shù)據(jù)類型轉(zhuǎn)化成了NSInputStream類型對(duì)象,所以這里只需要調(diào)用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) {
//如果當(dāng)前AFHTTPBodyPart是最后一個(gè)參數(shù),那么會(huì)比其他參數(shù)多一個(gè)--boundary--的部分,轉(zhuǎn)成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;
}
//將data讀入buffer中
- (NSInteger)readData:(NSData *)data
intoBuffer:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
/*沒(méi)有明白為什么讀data的時(shí)候range為什么要從_phaseReadOffset開(kāi)始,不應(yīng)該是從0開(kāi)始嗎,因?yàn)槊看?都是一個(gè)全新的data*/
NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length]
- ((NSUInteger)_phaseReadOffset), length));
//將data中range范圍之內(nèi)的數(shù)據(jù)復(fù)制到buffer里
[data getBytes:buffer range:range];
#pragma clang diagnostic pop
_phaseReadOffset += range.length;
if (((NSUInteger)_phaseReadOffset) >= [data length]) {
[self transitionToNextPhase];
}
return (NSInteger)range.length;
}
//序列化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];
}
//計(jì)算每個(gè)AFHTTPBodyPart的內(nèi)容長(zhǎng)度,包括傳輸參數(shù),請(qǐng)求頭和boundary信息,在AFMultiStream里會(huì)
//將所有的AFHTTPBodyPart的content-length加起來(lái),做為整個(gè)POST請(qǐng)求體的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;
}