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-urlencoded
和multipart/form-data
兩種序列化方式. 在application/x-www-form-urlencoded
模式下,請求體中會存放url encode之后的文本數(shù)據(jù). 而在multipart/form-data
模式下.請求體中是多段的數(shù)據(jù).