1. 程式人生 > >iOS學習----------AFNetworking(2)request建立和請求引數的序列化

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