AFNetworking之HTTP請求的序列化

AFURLRequestSerialization協(xié)議

AFURLRequestSerialization協(xié)議 是所有參數(shù)編碼器的抽象協(xié)議,此協(xié)議只有一個(gè)方法.

@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
/**將參數(shù)編碼到原請求的copy中并返回*/
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end

AFHTTPRequestSerializer

AFHTTPRequestSerializer是用來專門序列化HTTP請求的類.主要職責(zé)是創(chuàng)建經(jīng)過HTTP參數(shù)序列化構(gòu)造的 NSMutableRequest

AFHTTPRequestSerializer接口

@interface AFHTTPRequestSerializer:: NSObject <AFURLRequestSerialization>
/**
文本的編碼方案,默認(rèn)NSUTF8StringEncoding
 */
@property (nonatomic, assign) NSStringEncoding stringEncoding;

/**
是否允許使用蜂窩網(wǎng),默認(rèn)YES
 */
@property (nonatomic, assign) BOOL allowsCellularAccess;

/**
緩存策略,默認(rèn)YES
 */
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
/**
是否使用默認(rèn)的cookies 處理,默認(rèn)YES
 */
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;

/**
 在接收到相應(yīng)之前是否可以繼續(xù)發(fā)送數(shù)據(jù),默認(rèn)NO
 */
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;

/**
請求的網(wǎng)絡(luò)服務(wù)類型,默認(rèn)NSURLNetworkServiceTypeDefault
 */
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

/**
請求的超時(shí)時(shí)間,默認(rèn)60 seconds.
 */
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

/**
序列化請求的默認(rèn)HTTP頭域值,默認(rèn)包含:
 `Accept-Language`  內(nèi)容是 `NSLocale +preferredLanguages`,
`User-Agent`  內(nèi)容是操作系統(tǒng)名稱和各種包id
添加和移除頭信息使用 `setValue:forHTTPHeaderField:`方法
*/
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;

/**
實(shí)例化類方法
 */
+ (instancetype)serializer;

/**
設(shè)置 HTTP 頭信息,如果為'nil',則移除已經(jīng)存在的條目
 */
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;

/**
獲取HTTP headers中的信息
 */
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

/**
設(shè)置 有 HTTP 客戶端制造的 認(rèn)證 HTTP頭信息 ,使用 base64編碼的基礎(chǔ)用戶名和密碼認(rèn)證.
 */
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password;

/**
清除已經(jīng)存在的認(rèn)證HTTP頭信息.
 */
- (void)clearAuthorizationHeader;

/**
序列化后的請求會編碼參數(shù)作為查詢串的方法,默認(rèn)`GET`, `HEAD`, and `DELETE`
*/
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;

/**
設(shè)置query string 序列化的方式
 */
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;

/*
設(shè)置序列化block,用來自定義序列化query string
*/
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;

/**
使用指定的 HTTP方法和URL 創(chuàng)建 NSMutableURLRequest,如果HTTP是 `GET`, `HEAD`, or `DELETE`,參數(shù)會被編碼為 query string 附加到url上.否則 參數(shù)根據(jù) parameterEncoding 屬性進(jìn)行編碼 放到 請求體中.
 */
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(nullable id)parameters
                                     error:(NSError * _Nullable __autoreleasing *)error;
/**
使用指定的 HTTP方法和URL 創(chuàng)建 NSMutableURLRequest,并且構(gòu)造 `multipart/form-data` HTTP 請求體,使用指定的參數(shù)和 多部分表單數(shù)據(jù)block
多表單請求是自動(dòng)流式傳輸,直接從磁盤讀取文件和單個(gè)HTTPbody中的在內(nèi)存中的數(shù)據(jù),作為結(jié)果的 NSMutableURLRequest對象有個(gè)HTTPBodyStream 屬性,所以不要設(shè)置它的 HTTPBodyStream 或者  HTTPBody屬性,那會清除多表單體流.
 */
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(nullable NSDictionary <NSString *, id> *)parameters
                              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError * _Nullable __autoreleasing *)error;

/**
移除 request 中的 HTTPBodyStream 創(chuàng)建一個(gè)NSMutableURLRequest并返回.,將HTTPBodyStream中的內(nèi)容異步地寫它的內(nèi)容到指定的文件中,在完成時(shí)調(diào)用 completion handler .
這樣做的目的是為了解決`NSURLSessionTask`在設(shè)置流內(nèi)容消息體后不發(fā)送Content-length的問題(尤其是與Amaza S3 webservice通信時(shí)),被寫入后的文件可以被用來傳遞給`AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:` 或者 重新讀取為`NSData`設(shè)置到 HttpBody中.
 */
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(nullable void (^)(NSError * _Nullable error))handler;
@end

創(chuàng)建序列化參數(shù)到body中的請求

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;
    
    //設(shè)置一些屬性的值
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
    //這里傳遞了parameters的值會將 參數(shù)序列化為url encode的形式放到消息體中
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(NSDictionary *)parameters
                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    //GET和 HEAD 方法不能將參數(shù)序列化到 HTTP body中
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

    //獲得了設(shè)置了頭信息, parameters 參數(shù)傳遞了nil 所以請求體中沒有參數(shù)
    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

    //創(chuàng)建多部分表單數(shù)據(jù)
    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

    if (parameters) {
        //遍歷所有的參數(shù)值,將序列化好的 key value 添加在formData 中
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
           //將parameters 中的value 轉(zhuǎn)化為二進(jìn)制
            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];
            }
            //將某一數(shù)據(jù)添加到form data中
            if (data) {
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }
    //調(diào)用外界構(gòu)造http body 閉包
    if (block) {
        block(formData);
    }

    //返回最終的 NSMutableURLRequest
    return [formData requestByFinalizingMultipartFormData];
}

- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(void (^)(NSError *error))handler
{
    //參數(shù)合理性判斷
    NSParameterAssert(request.HTTPBodyStream);
    NSParameterAssert([fileURL isFileURL]);
    
    //獲取出請求中的 HTTPBodayStream
    NSInputStream *inputStream = request.HTTPBodyStream;
    //創(chuàng)建使用文件路徑 OuputStream
    NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO];
    __block NSError *error = nil;
  
    //全局隊(duì)列異步執(zhí)行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //將讀取流與寫入流添加到 run loop中
        [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

        [inputStream open];
        [outputStream open];

        while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {
            //創(chuàng)建大小為1K的緩沖區(qū)
            uint8_t buffer[1024];
            //讀取數(shù)據(jù)到buffer中
            NSInteger bytesRead = [inputStream read:buffer maxLength:1024];
            //讀取發(fā)生錯(cuò)誤則返回失敗
            if (inputStream.streamError || bytesRead < 0) {
                error = inputStream.streamError;
                break;
            }
            //將buffer中的數(shù)據(jù)寫入文件
            NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];
            //發(fā)生錯(cuò)誤則直接返回
            if (outputStream.streamError || bytesWritten < 0) {
                error = outputStream.streamError;
                break;
            }
            
            //沒有數(shù)據(jù)可以讀寫跳出循環(huán)
            if (bytesRead == 0 && bytesWritten == 0) {
                break;
            }
        }

        //關(guān)閉流
        [outputStream close];
        [inputStream close];

        if (handler) {
            //mian queue 異步調(diào)用完成回調(diào)
            dispatch_async(dispatch_get_main_queue(), ^{
                handler(error);
            });
        }
    });
    
    //拷貝原請求并清空 HTTPBodyStream
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    mutableRequest.HTTPBodyStream = nil;

    return mutableRequest;
}

請求序列化之 AFMultipartFormData 協(xié)議

AFMultipartFormData 協(xié)議定義了構(gòu)造多表單每個(gè)條目的接口

@protocol AFMultipartFormData
/**
添加HTTP頭 Content-Disposition: file; filename=#{generated filename}; name=#{name} 和 Content-Type: #{generated mimeType},后面根 編碼過后的數(shù)據(jù),和多表單邊界.
數(shù)據(jù)的filename 和 MIME type會 使用fileURL的最后一個(gè)部分和系統(tǒng)相關(guān)的MIME type 自動(dòng)生成,
 */
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                        error:(NSError * _Nullable __autoreleasing *)error;

/*添加HTTP頭 Content-Disposition: file; filename=#{generated filename}; name=#{name} 和 Content-Type: #{generated mimeType},后面根 編碼過后的數(shù)據(jù),和多表單邊界. 
此方法是可以設(shè)置mimeType版本
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                     fileName:(NSString *)fileName
                     mimeType:(NSString *)mimeType
                        error:(NSError * _Nullable __autoreleasing *)error;
/*
使用 inputStream 添加HTTP表單信息
*/
- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
                             name:(NSString *)name
                         fileName:(NSString *)fileName
                           length:(int64_t)length
                         mimeType:(NSString *)mimeType;
/*
使用NSData 構(gòu)建HTTP表單信息
*/
- (void)appendPartWithFileData:(NSData *)data
                          name:(NSString *)name
                      fileName:(NSString *)fileName
                      mimeType:(NSString *)mimeType;
- (void)appendPartWithFormData:(NSData *)data
                          name:(NSString *)name;
/*
使用自定義的頭和body構(gòu)建HTTP表單信息
*/
- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers
                         body:(NSData *)body;

/*
通過限制數(shù)據(jù)包大小和為從上載流讀取的每個(gè)數(shù)據(jù)塊添加延遲來限制請求帶寬烈和。
當(dāng)通過3G或邊緣連接上載時(shí)氓润,請求可能會失敗馋艺,并顯示“請求正文流已耗盡”掠抬。根據(jù)推薦值設(shè)置最大包大小和延遲(‘KAFPuxAdvRAD3GGGGESTEDPACKETSIZE’和‘KAFPuxAdRead 3GugGeGestDeld'’)降低了輸入流超過其分配帶寬的風(fēng)險(xiǎn)闸与。不幸的是,在“NSURLConnection”上沒有明確的方法來區(qū)分3G涉兽、EDGE或LTE連接嗤谚。因此,不建議僅基于網(wǎng)絡(luò)可達(dá)性來限制帶寬藏畅。相反敷硅,您應(yīng)該考慮檢查故障塊中的“請求正文流已耗盡”功咒,然后使用帶寬限制重試請求。


PARAM數(shù)字節(jié)最大包大小绞蹦,字節(jié)數(shù)力奋。輸入流的默認(rèn)數(shù)據(jù)包大小為16kb。

@參數(shù)每次讀取數(shù)據(jù)包時(shí)的延遲持續(xù)時(shí)間幽七。默認(rèn)情況下景殷,不設(shè)置延遲。
*/
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
                                  delay:(NSTimeInterval)delay;
@end

AFStreamingMultipartFormData

AFStreamingMultipartFormData是對HTTP請求多表單數(shù)據(jù)的抽象,實(shí)現(xiàn)了 AFMultipartFormData協(xié)議封裝了HTTP 每個(gè)表單條目的構(gòu)造過程.

@interface AFStreamingMultipartFormData : NSObject <AFMultipartFormData>
//使用 request 和 編碼方案 創(chuàng)建數(shù)據(jù)
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
                    stringEncoding:(NSStringEncoding)encoding;
//得到最終的帶多表單數(shù)據(jù)的請求
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;
@end

@implementation AFStreamingMultipartFormData
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                     fileName:(NSString *)fileName
                     mimeType:(NSString *)mimeType
                        error:(NSError * __autoreleasing *)error
{
    NSParameterAssert(fileURL);
    NSParameterAssert(name);
    NSParameterAssert(fileName);
    NSParameterAssert(mimeType);
    //當(dāng)URL 不是 file url 的情況 返回失敗
    if (![fileURL isFileURL]) {
        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        return NO;
        //文件不能被獲取返回失敗
    } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        return NO;
    }

    //獲取文件信息
    NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
    if (!fileAttributes) {
        return NO;
    }
    
    //構(gòu)造表單頭信息
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];

    //創(chuàng)建 AFHTTPBodyPart body的抽象,添加到 bodyStream中
    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = mutableHeaders;
    bodyPart.boundary = self.boundary;
    bodyPart.body = fileURL;
    bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue];
    [self.bodyStream appendHTTPBodyPart:bodyPart];

    return YES;
}

- (void)appendPartWithInputStream:(NSInputStream *)inputStream
                             name:(NSString *)name
                         fileName:(NSString *)fileName
                           length:(int64_t)length
                         mimeType:(NSString *)mimeType
{
    NSParameterAssert(name);
    NSParameterAssert(fileName);
    NSParameterAssert(mimeType);

    //構(gòu)造表單段頭信息
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];

    //創(chuàng)建 AFHTTPBodyPart body的抽象,添加到 bodyStream中
    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = mutableHeaders;
    bodyPart.boundary = self.boundary;
    bodyPart.body = inputStream;

    bodyPart.bodyContentLength = (unsigned long long)length;

    [self.bodyStream appendHTTPBodyPart:bodyPart];
}


- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
    if ([self.bodyStream isEmpty]) {
        return self.request;
    }

    // Reset the initial and final boundaries to ensure correct Content-Length
    // 重新設(shè)置 初始的和最終的boundaries 確保有正確的 content-length
    [self.bodyStream setInitialAndFinalBoundaries];
    //設(shè)置HTTP請求體
    [self.request setHTTPBodyStream:self.bodyStream];

    //重新設(shè)置請求頭部的 Content-Type
    [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
    //重新設(shè)置請求頭部的 Content-Length
    [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];

    return self.request;
}

@end

AFMultipartBodyStream

@interface  AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
//每個(gè)段的大小
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
//延時(shí)時(shí)間 在 3G 和 2G 下控制網(wǎng)絡(luò)擁塞
@property (nonatomic, assign) NSTimeInterval delay;
// ???
@property (nonatomic, strong) NSInputStream *inputStream;
//內(nèi)容的長度
@property (readonly, nonatomic, assign) unsigned long long contentLength;
//是否為空
@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;

//使用編碼方案初始化
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
//標(biāo)記初始和最后的段
- (void)setInitialAndFinalBoundaries;
//添加多表單數(shù)據(jù)條目
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
@end

主要的方法實(shí)現(xiàn)

- (void)setInitialAndFinalBoundaries {
    if ([self.HTTPBodyParts count] > 0) {
        //遍歷 AFHTTPBodyPart 數(shù)組,設(shè)置 bodyPart的 hasInitialBoundary 和 hasFinalBoundary 為NO
        for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
            bodyPart.hasInitialBoundary = NO;
            bodyPart.hasFinalBoundary = NO;
        }
        //標(biāo)記第一個(gè) boundary
        [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
        //標(biāo)記最后一個(gè) boundary
        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
    }
}

- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
    //將bodyPart添加到數(shù)組中
    [self.HTTPBodyParts addObject:bodyPart];
}

#pragma mark - NSInputStream

- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length
{
    //inputstream 沒有打開則直接關(guān)閉
    if ([self streamStatus] == NSStreamStatusClosed) {
        return 0;
    }

    NSInteger totalNumberOfBytesRead = 0;

    while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
        // currentHTTPBodyPart為空 或者 currentHTTPBodyPart 沒有可用字節(jié)
        if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
            
            //下一個(gè)AFHTTPBodyPart為空則跳出循環(huán)
            if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
                break;
            }
        } else {
            //獲取還剩下多少字節(jié)沒有讀
            NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
            //調(diào)用 AFHTTPBodyPart 的讀方法
            NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
            //發(fā)生了錯(cuò)誤
            if (numberOfBytesRead == -1) {
                self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
                break;
            } else {
                //計(jì)算總共讀取的字節(jié)數(shù)
                totalNumberOfBytesRead += numberOfBytesRead;

                //設(shè)置了延時(shí)時(shí)間則延時(shí)
                if (self.delay > 0.0f) {
                    [NSThread sleepForTimeInterval:self.delay];
                }
            }
        }
    }

    return totalNumberOfBytesRead;
}
- (BOOL)getBuffer:(__unused uint8_t **)buffer
           length:(__unused NSUInteger *)len
{
    //不支持
    return NO;
}

- (BOOL)hasBytesAvailable {
    //只有在讀取時(shí),才能知道是否有剩余字節(jié)
    return [self streamStatus] == NSStreamStatusOpen;
}

#pragma mark - NSStream

- (void)open {
    //已經(jīng)是Open狀態(tài)直接返回
    if (self.streamStatus == NSStreamStatusOpen) {
        return;
    }
    
    //設(shè)置當(dāng)前狀態(tài)為打開
    self.streamStatus = NSStreamStatusOpen;
    
    //標(biāo)記開始和結(jié)束的bodyPart
    [self setInitialAndFinalBoundaries];
    
    //初始化迭代器
    self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}

- (void)close {
    //標(biāo)記當(dāng)前狀態(tài)為關(guān)閉
    self.streamStatus = NSStreamStatusClosed;
}

//不支持
- (id)propertyForKey:(__unused NSString *)key {
    return nil;
}

//不支持
- (BOOL)setProperty:(__unused id)property
             forKey:(__unused NSString *)key
{
    return NO;
}

//不支持
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

//不支持
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

AFHTTPBodyPart

AFHTTPBodyPart 提供了讀取多表單條目數(shù)據(jù)的方法,其中每個(gè)條目的body部分使用NSInputStream讀取.
**AFHTTPBodyPart 接口 **

@interface AFHTTPBodyPart : NSObject
//編碼方案
@property (nonatomic, assign) NSStringEncoding stringEncoding;
//頭信息
@property (nonatomic, strong) NSDictionary *headers;
//分割符
@property (nonatomic, copy) NSString *boundary;
//消息體數(shù)據(jù)
@property (nonatomic, strong) id body;
//消息體數(shù)據(jù)大小
@property (nonatomic, assign) unsigned long long bodyContentLength;
//輸入流
@property (nonatomic, strong) NSInputStream *inputStream;

//是否為起始bodypart,需要對 boundary進(jìn)行一點(diǎn)處理
@property (nonatomic, assign) BOOL hasInitialBoundary;
//是否為最后一個(gè)bodypart, 需要對boundary 進(jìn)行處理
@property (nonatomic, assign) BOOL hasFinalBoundary;

//是否有可用字節(jié)
@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable;
//總大小
@property (readonly, nonatomic, assign) unsigned long long contentLength;

//讀數(shù)據(jù)放到buffer中
- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length;
@end

post請求body樣本 (multipart/form-data)

 /*
     一個(gè)完整的段是結(jié)構(gòu):
      第一個(gè)段 --boundary\r\n ,中間的段\r\n--boundary\r\n
      <--headr info--!> \r\n
      <--headr info--!> \r\n \r\n
      <--body--!>
      如果是最后一個(gè)段要加上\r\n--boundary--\r\n
     */
----------------------------798861386718386465039906
Content-Disposition: form-data; name="para0"

123456
----------------------------798861386718386465039906
Content-Disposition: form-data; name="para2"

123456
----------------------------798861386718386465039906--

AFHTTPBodyPart主要方法

- (BOOL)transitionToNextPhase {
    //在主線程執(zhí)行
    if (![[NSThread currentThread] isMainThread]) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self transitionToNextPhase];
        });
        return YES;
    }

    switch (_phase) {
            // AFEncapsulationBoundaryPhase -> AFHeaderPhase
        case AFEncapsulationBoundaryPhase:
            _phase = AFHeaderPhase;
            break;
            // AFHeaderPhase -> AFHeaderPhase
        case AFHeaderPhase:
            //設(shè)置input stream 在 runloop中調(diào)度
            [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
             //設(shè)置input stream 打開
            [self.inputStream open];
            _phase = AFBodyPhase;
            break;
            //AFBodyPhase -> AFFinalBoundaryPhase
        case AFBodyPhase:
            //關(guān)閉流
            [self.inputStream close];
            _phase = AFFinalBoundaryPhase;
            break;
        case AFFinalBoundaryPhase:
        default:
            _phase = AFEncapsulationBoundaryPhase;
            break;
    }
    //重置段讀取索引
    _phaseReadOffset = 0;

    return YES;
}

- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length
{
    NSInteger totalNumberOfBytesRead = 0;
    
    //讀取 boundary到buffer中
    if (_phase == AFEncapsulationBoundaryPhase) {
        NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
        totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }
    
    //讀取頭信息數(shù)據(jù)到 buffer中
    if (_phase == AFHeaderPhase) {
        NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
        totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }

    //讀取body數(shù)據(jù)到 buffer中
    if (_phase == AFBodyPhase) {
        NSInteger numberOfBytesRead = 0;
       
        //使用input stream 讀取  body數(shù)據(jù)到buffer中
        numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
        if (numberOfBytesRead == -1) {
            return -1;
        } else {
            totalNumberOfBytesRead += numberOfBytesRead;
             //body數(shù)據(jù)讀取完成進(jìn)入下一個(gè)狀態(tài)
            if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
                [self transitionToNextPhase];
            }
        }
    }
    //讀取最后一個(gè)段的boundary,到buffer中
    if (_phase == AFFinalBoundaryPhase) {
        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;
}

- (NSInteger)readData:(NSData *)data
           intoBuffer:(uint8_t *)buffer
            maxLength:(NSUInteger)length
{
    //data中 根據(jù) _phaseReadOffset 讀取一段數(shù)據(jù)
    NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
    [data getBytes:buffer range:range];
    
    //更新偏移
    _phaseReadOffset += range.length;
    
    //如果data已經(jīng)被讀取完畢,跳轉(zhuǎn)到下一個(gè)狀態(tài)
    if (((NSUInteger)_phaseReadOffset) >= [data length]) {
        [self transitionToNextPhase];
    }

    return (NSInteger)range.length;
}

對于POST請求,AFHTTPRequestSerializer內(nèi)部支持application/x-www-form-urlencodedmultipart/form-data兩種序列化方式. 在application/x-www-form-urlencoded模式下,請求體中會存放url encode之后的文本數(shù)據(jù). 而在multipart/form-data模式下.請求體中是多段的數(shù)據(jù).

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澡屡,一起剝皮案震驚了整個(gè)濱河市猿挚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驶鹉,老刑警劉巖绩蜻,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異室埋,居然都是意外死亡办绝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門姚淆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孕蝉,“玉大人,你說我怎么就攤上這事腌逢∥羟” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵上忍,是天一觀的道長。 經(jīng)常有香客問我纳本,道長窍蓝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任繁成,我火速辦了婚禮吓笙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巾腕。我一直安慰自己面睛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布尊搬。 她就那樣靜靜地躺著叁鉴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪佛寿。 梳的紋絲不亂的頭發(fā)上幌墓,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼常侣。 笑死蜡饵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胳施。 我是一名探鬼主播溯祸,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舞肆!你這毒婦竟也來了焦辅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤胆绊,失蹤者是張志新(化名)和其女友劉穎氨鹏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體压状,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仆抵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了种冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镣丑。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖娱两,靈堂內(nèi)的尸體忽然破棺而出莺匠,到底是詐尸還是另有隱情,我是刑警寧澤十兢,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布趣竣,位于F島的核電站,受9級特大地震影響旱物,放射性物質(zhì)發(fā)生泄漏遥缕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一宵呛、第九天 我趴在偏房一處隱蔽的房頂上張望单匣。 院中可真熱鬧,春花似錦宝穗、人聲如沸户秤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸡号。三九已至,卻和暖如春须鼎,著一層夾襖步出監(jiān)牢的瞬間膜蠢,已是汗流浹背堪藐。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挑围,地道東北人礁竞。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像杉辙,于是被迫代替她去往敵國和親模捂。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

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