博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AFNetworking-RequestSerializer
阅读量:5756 次
发布时间:2019-06-18

本文共 16193 字,大约阅读时间需要 53 分钟。

概况介绍:

这篇主要介绍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是最重要也是最复杂的部分,源码也主要针对这部分做分析。

源码分析:

参数序列化和编码

- (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--复制代码
  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。其结构示意如下:

//处理multilpart/form-data(POST)请求- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method                                               URLString:(NSString *)URLString                                              parameters:(NSDictionary *)parameters                               constructingBodyWithBlock:(void (^)(id 
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; }复制代码

转载于:https://juejin.im/post/5b68180c6fb9a04fd6597518

你可能感兴趣的文章
标准与扩展ACL 、 命名ACL 、 总结和答疑
查看>>
查找恶意的TOR中继节点
查看>>
MAVEN 属性定义与使用
查看>>
shell高级视频答学生while循环问题
查看>>
使用@media实现IE hack的方法
查看>>
《11招玩转网络安全》之第一招:Docker For Docker
查看>>
hive_0.11中文用户手册
查看>>
hiveserver2修改线程数
查看>>
XML教程
查看>>
oracle体系结构
查看>>
Microsoft Exchange Server 2010与Office 365混合部署升级到Exchange Server 2016混合部署汇总...
查看>>
Proxy服务器配置_Squid
查看>>
开启“无线网络”,提示:请启动windows零配置wzc服务
查看>>
【SDN】Openflow协议中对LLDP算法的理解--如何判断非OF区域的存在
查看>>
纯DIV+CSS简单实现Tab选项卡左右切换效果
查看>>
栈(一)
查看>>
ios 自定义delegate(一)
查看>>
创建美国地区的appleId
查看>>
例题10-2 UVa12169 Disgruntled Judge(拓展欧几里德)
查看>>
JS 原生ajax写法
查看>>