AFNetworking-RequestSerializer

概況介紹:

這篇主要介紹AFNetworking中請(qǐng)求參數(shù)序列化的部分,具體代碼在AFURLRequestSerialization中。AFURLRequestSerialization包含四部分:

  • AFHTTPRequestSerializaiton
  • AFJSONRequestSerializer
  • AFPropertyListRequestSerializer

AFHTTPRequestSerialization主要是設(shè)置http請(qǐng)求頭,設(shè)置超時(shí)時(shí)間,BA認(rèn)證,處理用戶名密碼登陸等等。主要功能分3大塊:

  1. 處理所有的GET,HEAD,DELETE請(qǐng)求
  2. 處理content-type是application/x-www-form-urlencoded類型的POST請(qǐng)求
  3. 處理content-type是multipart/form-data類型的POST請(qǐng)求,請(qǐng)求的構(gòu)建是通過(guò)AFStreamingMultipartFormData對(duì)象實(shí)現(xiàn)的。

AFJSONRequestSerializer繼承自AFHTTPRequestSerialization類,使用NSJSONSerialization序列化json格式(application/json)的參數(shù),將一個(gè)Dictionary對(duì)象轉(zhuǎn)化成NSData,它只處理POST請(qǐng)求。

AFPropertyListRequestSerializer也繼承了AFHTTPRequestSerialization類,使用NSPropertyListSerialization對(duì)象來(lái)序列化xml格式(application/x-plist)的參數(shù),它也只處理POST請(qǐng)求。

綜上所述,AFHTTPRequestSerialization是最重要也是最復(fù)雜的部分,源碼也主要針對(duì)這部分做分析。

源碼分析:

參數(shù)序列化和編碼

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
 
                               withParameters:(id)parameters
 
                                        error:(NSError *__autoreleasing *)error
 
{
 
    NSParameterAssert(request);
 
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
//設(shè)置http請(qǐng)求頭
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id 
        value, BOOL * __unused stop) {
 
        if (![request valueForHTTPHeaderField:field]) {
 
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
//序列化參數(shù)
    if (parameters) {
 
        NSString *query = nil;
//queryStringSerilization是一個(gè)block,主要是用來(lái)自定義參數(shù)序列化的邏輯,返回一個(gè)序列化完成的結(jié)果
        if (self.queryStringSerialization) {
 
            NSError *serializationError;
 
            query = self.queryStringSerialization(request, parameters, &
                serializationError);
 
            if (serializationError) {
 
                if (error) {
 
                    *error = serializationError;
 
                }
 
                return nil;
            }
        } else {
 
            switch (self.queryStringSerializationStyle) {
//使用AFNetworking默認(rèn)的格式序列化,a=1&b=2這種
                case AFHTTPRequestQueryStringDefaultStyle:
//這里parameters是一個(gè)dictionary對(duì)象,stringEncoding是給httpBody設(shè)置data的時(shí)候字符
//串的編碼格式
                    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接受一個(gè)NSData的參數(shù),將query轉(zhuǎn)化成NSData
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
    }
 
    return mutableRequest;
}
//用于AFURLRequestSerialization內(nèi)部調(diào)用的方法
static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {
 
    NSMutableArray *mutablePairs = [NSMutableArray array];
//AFQueryStringPair是封裝的鍵值對(duì)對(duì)象,主要是將dictionary中的鍵值對(duì)轉(zhuǎn)化成query 
//string形式,包括一些特殊字符的編碼
//AFQueryStringPairsFromDictionary將dictionary轉(zhuǎn)化成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)];
    }
}
//主要調(diào)用foundation函數(shù)CFURLCreateStringByAddingPercentEscapes,它主要是將querystri
//ng中的特殊字符(&,?)編碼成“%+ASCII” 形式。根據(jù)文檔,建議使用NSString 
//stringByAddingPercentEncodingWithAllowedCharacters:]方法,這個(gè)方法使用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

先看一個(gè)mutipart/form-data格式的請(qǐng)求

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一般是隨機(jī)生成的,保持唯一即可。上面還需要有content-length,圖里面沒(méi)有。
  2. 每個(gè)參數(shù)的部分都有content-disposition,并且以--boundary開(kāi)頭,最后一個(gè)參數(shù)以--boundary--結(jié)尾。

AFNetworking通過(guò)AFStreamingMutipartFormData處理multipart/form-data格式的POST請(qǐng)求,它通過(guò)NSInputStream來(lái)構(gòu)建http請(qǐng)求的body。每個(gè)參數(shù)的信息封裝到了AFHTTPBodyPart中,所有的參數(shù)信息封裝在AFMulipartBodyStream中。AFHTTPBodyPart中有NSInputStream對(duì)象用來(lái)將每個(gè)參數(shù)的寫入到body,而AFMutipartBodyStream繼承子NSInputStream處理所有參數(shù)的寫入到body。其結(jié)構(gòu)示意如下:

AFNetworkingArch
//處理multilpart/form-data(POST)請(qǐng)求
- (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,因?yàn)楫?dāng)前格式(mutilpart/form-data)
//需要由AFStreamingMulitpartFormData去構(gòu)建參數(shù)。
    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)) {
//對(duì)于dictionary的參數(shù),直接將它的值轉(zhuǎn)成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對(duì)象的appendPartWithFormData將鍵值對(duì)轉(zhuǎn)化成AFHTTPBodyP
//art并且放到AFMultipartBodyStream集合中。
                [formData appendPartWithFormData:data name:[pair.field description]];
 
            }
 
        }
 
    }
 
    if (block) {
//當(dāng)前block用來(lái)處理非dictionary的情況,比如參數(shù)可能是一個(gè)NSURL,或者直接就是一個(gè)NSInputStream。
        block(formData);
 
    }
//AFStreamingMultipartFormData的requestByFinalizingMultipartFormData主要是設(shè)置cont
//ent-Type和content-Length以及boundary
    return [formData requestByFinalizingMultipartFormData];
 
}
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
 
    if ([self.bodyStream isEmpty]) {
 
        return self.request;
 
    }
 
    // 設(shè)置boundary
 
    [self.bodyStream setInitialAndFinalBoundaries];
 
//將AFStreamingMultipartFormData的AFMultipartBodyStream設(shè)置到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;
 
        }
//設(shè)置boundary的頭
        [[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES];
//設(shè)置boundary的尾
        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
 
    }
 
}
//主要是設(shè)置每個(gè)參數(shù)部分的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];
 
}
//構(gòu)建AFHTTPBodyPart對(duì)象,這種情況下,AFHTTPBody對(duì)象的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去構(gòu)建AFHTTPBodyPart對(duì)象,過(guò)程和NSData類似,設(shè)置Content-disposition和Content-Type,再通過(guò)data去構(gòu)建AFHTTPBodyPart。

AFMultipartBodyStream

- (NSInteger)read:(uint8_t *)buffer
 
        maxLength:(NSUInteger)length
{
//AFStreamingMutipartFormData將AFMultipartBodyStream設(shè)置到NSUrlRequest的httpbodys
//tream之后,foundation會(huì)自動(dòng)調(diào)用read:maxLength:方法,改方法實(shí)現(xiàn)中遍歷之前構(gòu)建的所有AFHTTP
//BodyPart對(duì)象,分別調(diào)用它們的read:maxLength:方法來(lái)獲取數(shù)據(jù)。


    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

//根據(jù)body類型生成對(duì)應(yīng)的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部分,轉(zhuǎn)成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,轉(zhuǎn)成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;
//每個(gè)AFHTTPBodyPart的body部分,也就是實(shí)際傳輸?shù)臄?shù)據(jù)部分,都通過(guò)在inputStream方法里根據(jù)其實(shí)
//際數(shù)據(jù)類型轉(zhuǎn)化成了NSInputStream類型對(duì)象,所以這里只需要調(diào)用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) {
//如果當(dāng)前AFHTTPBodyPart是最后一個(gè)參數(shù),那么會(huì)比其他參數(shù)多一個(gè)--boundary--的部分,轉(zhuǎn)成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"
/*沒(méi)有明白為什么讀data的時(shí)候range為什么要從_phaseReadOffset開(kāi)始,不應(yīng)該是從0開(kāi)始嗎,因?yàn)槊看?都是一個(gè)全新的data*/
    NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length]
     - ((NSUInteger)_phaseReadOffset), length));
//將data中range范圍之內(nèi)的數(shù)據(jù)復(fù)制到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];
 
} 
//計(jì)算每個(gè)AFHTTPBodyPart的內(nèi)容長(zhǎng)度,包括傳輸參數(shù),請(qǐng)求頭和boundary信息,在AFMultiStream里會(huì)
//將所有的AFHTTPBodyPart的content-length加起來(lái),做為整個(gè)POST請(qǐng)求體的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;
 
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

推薦閱讀更多精彩內(nèi)容