概况介绍:
这篇主要介绍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.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="text" title------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-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。其结构示意如下:
//处理multilpart/form-data(POST)请求- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(idformData))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; }复制代码