美文网首页
AFNetworking-RequestSerializer

AFNetworking-RequestSerializer

作者: 闹鬼的金矿 | 来源:发表于2017-07-21 16:22 被阅读88次

    概况介绍:

    这篇主要介绍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.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--
    
    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。其结构示意如下:

    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;
     
    }
    

    相关文章

      网友评论

          本文标题:AFNetworking-RequestSerializer

          本文链接:https://www.haomeiwen.com/subject/wxyekxtx.html