概况介绍:
这篇主要介绍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是最重要也是最复杂的部分,源码也主要针对这部分做分析。
源码分析:
参数序列化和编码
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)); }
|
先看一个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--
|
- 它的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。其结构示意如下:
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; }
|