iOS學習----------AFNetworking(2)request建立和請求引數的序列化
為了迎合iOS新版本的升級, AFNetworking在3.0版本中刪除了基於 NSURLConnection API的所有支援。如果你的專案以前使用過這些API,建議您立即升級到基於 NSURLSession 的API的AFNetworking的版本。遷移方法:AFNetworking 3.0遷移指南
AFNetworking原始碼大致分為以下幾個部分:
網路監控:AFNetworkReachabilityManager
網路安全策略:AFSecurityPolicy
請求資料序列化:AFURLRequestSerialization
響應資料序列化:AFURLResponseSerialization
網路請求管理:AFURLSessionManager(AFHTTPSessionManager繼承於AFURLSessionManager)
本部分主要介紹請求資料序列化同時包含request的建立(AFURLRequestSerialization)
AFURLRequestSerialization.h檔案
協議:AFURLRequestSerialization
協議方法:
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *) error NS_SWIFT_NOTHROW;
協議:AFMultipartFormData
方法:
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * _Nullable __autoreleasing *)error;
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(int64_t)length
mimeType:(NSString *)mimeType;
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name;
- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers body:(NSData *)body;
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay;
類:AFHTTPRequestSerializer、AFPropertyListRequestSerializer
AFURLRequestSerialization.m檔案
建立NSMutableURLRequest物件,從第一個方法入手
/**
使用指定的HTTP method和URLString來構建一個NSMutableURLRequest物件例項
如果method是GET、HEAD、DELETE,那parameter將會被用來構建一個基於url編碼的查詢字串(query url)
,並且這個字串會直接加到request的url後面。對於其他的Method,比如POST/PUT,它們會根
據parameterEncoding屬性進行編碼,而後加到request的http body上。
@param method request的HTTP methodt,比如 `GET`, `POST`, `PUT`, or `DELETE`. 該引數不能為空
@param URLString 用來建立request的URL
@param parameters 既可以對method為GET的request設定一個查詢字串(query string),也可以設定到request的HTTP body上
@param error 構建request時發生的錯誤
@return 一個NSMutableURLRequest的物件
*/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error
{
//斷言
//方法或函式應當在程式碼最開始處使用 NSParameterAssert / NSCParameterAssert 來強制輸入的值滿足先驗條件,這是一條金科玉律;其他情況下使用 NSAssert / NSCAssert。
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
//建立mutableRequest病指定請求方法
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
//通過迴圈將本類中的幾個keypath設定在mutableRequest中
//AFHTTPRequestSerializerObservedKeyPaths()方法為初始化各個key path
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//使用kvc的方式設定mutableRequest的屬性
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//設定request的http header field 和序列化引數
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
AFHTTPRequestSerializerObservedKeyPaths()方法,將本類中的key path(就是屬性名)蒐集在一起
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
這幾種keypath的意思如下:
allowsCellularAccess: 是否允許使用裝置的蜂窩行動網路來建立request,預設為允許
cachePolicy: 建立的request所使用的快取策略,預設使用NSURLRequestUseProtocolCachePolicy
,該策略表示,如果快取不存在,直接從服務端獲取。如果快取存在,會根據response中的Cache-Control欄位判斷,下一步操作,如: Cache-Control欄位為must-revalidata, 則 詢問服務端該資料是否有更新,無更新話直接返回給使用者快取資料,若已更新,則請求服務端.
HTTPShouldHandleCookies:
如果設定HTTPShouldHandleCookies為YES,就處理儲存在NSHTTPCookieStore中的cookies
HTTPShouldHandleCookies表示是否應該給request設定cookie並隨request一起傳送出去
HTTPShouldUsePipelining:HTTPShouldUsePipelining表示receiver(理解為iOS客戶端)的下一個資訊是否必須等到上一個請求回覆才能傳送。
如果為YES表示可以,NO表示必須等receiver收到先前的回覆才能傳送下個資訊。
networkServiceType: 設定request的network service型別. 預設是NSURLNetworkServiceTypeDefault
.
這個network service是為了告訴系統網路層這個request使用的目的 比如NSURLNetworkServiceTypeVoIP表示的就這個request是用來請求網際協議通話技術(Voice over IP)。
系統能根據提供的資訊來優化網路處理,從而優化電池壽命,網路效能等等
timeoutInterval:超時時間 60秒
第二部分程式碼--設定request的header field 和序列化引數(get 、head 、delete這三種方法建立request)
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error
{
//request不能為空
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
//自定義序列化的block,如果使用者實現了這個方法就呼叫這個block
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
//使用afn內部的序列化方法
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
//將序列化後的引數與url進行拼接
//HTTPMethodsEncodingParametersInURI:GET, HEAD, DELETE
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
/*mutableRequest.URL.query ? @"&%@" : @"?%@", query判斷後面是否有引數,如果有引數 用&號拼接 如果沒有引數使用?拼接 */
if (query) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
AFN內部的序列化方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
根本方法為:
序列化請求引數
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
// 遞迴解析資料,baz[]=1&baz[]=2&baz[]=3&foo=bar
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}