概况介绍:
这篇主要介绍AFNetworking中请求参数序列化的部分,具体代码在AFURLRequestSerialization中。AFURLRequestSerialization包含四部分:
- AFHTTPRequestSerializaiton
- AFJSONRequestSerializer
- AFPropertyListRequestSerializer
AFHTTPRequestSerialization主要是设置http请求头,设置超时时间,BA认证,处理用户名密码登陆等等。主要功能分3大块:
- 处理所有的GET,HEAD,DELETE请求
- 处理content-type是application/x-www-form-urlencoded类型的POST请求
- 处理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是最重要也是最复杂的部分,源码也主要针对这部分做分析。
源码分析:
参数序列化和编码
- (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;
}
//用于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:@"&"];
}
- (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)];
}
}
//主要调用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格式的请求
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一般是随机生成的,保持唯一即可。上面还需要有content-length,图里面没有。
- 每个参数的部分都有content-disposition,并且以--boundary开头,最后一个参数以--boundary--结尾。
AFNetworking通过AFStreamingMutipartFormData处理multipart/form-data格式的POST请求,它通过NSInputStream来构建http请求的body。每个参数的信息封装到了AFHTTPBodyPart中,所有的参数信息封装在AFMulipartBodyStream中。AFHTTPBodyPart中有NSInputStream对象用来将每个参数的写入到body,而AFMutipartBodyStream继承子NSInputStream处理所有参数的写入到body。其结构示意如下:
AFNetworkingArch//处理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];
}
- (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;
}
- (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];
}
}
//主要是设置每个参数部分的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];
}
//构建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
- (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
//根据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;
}
- (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;
}
//将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;
}
//序列化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];
}
//计算每个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;
}
网友评论