AFNetworking源碼閱讀4——請(qǐng)求序列化

前言

序列化,百度百科的解釋摘抄如下:

序列化 (Serialization)將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程。在序列化期間挽霉,對(duì)象將其當(dāng)前狀態(tài)寫入到臨時(shí)或持久性存儲(chǔ)區(qū)写穴。以后,可以通過從存儲(chǔ)區(qū)中讀取或反序列化對(duì)象的狀態(tài)判没,重新創(chuàng)建該對(duì)象。
1隅茎、以某種存儲(chǔ)形式使自定義對(duì)象持久化澄峰;
2、將對(duì)象從一個(gè)地方傳遞到另一個(gè)地方辟犀。
3俏竞、使程序更具維護(hù)性。

簡(jiǎn)單點(diǎn)說就是將對(duì)象轉(zhuǎn)換為二進(jìn)制流堂竟,以便存儲(chǔ)或傳輸魂毁,且日后也能從二進(jìn)制流轉(zhuǎn)換回對(duì)象。 所以我們今天要說的請(qǐng)求序列化跃捣,說到底就是造一個(gè)可以在網(wǎng)絡(luò)上傳遞(被序列化)的漱牵、合法可用的請(qǐng)求。這其中就包括了參數(shù)的處理請(qǐng)求頭的設(shè)置等疚漆。不管怎樣的代碼酣胀,萬變不離其宗,它的核心就是這個(gè)知識(shí)點(diǎn)娶聘。所以理解了這個(gè)闻镶,再去看代碼就容易多了。
我們知道丸升,GET請(qǐng)求和POST請(qǐng)求對(duì)于參數(shù)是不同的處理铆农。GET請(qǐng)求是多個(gè)參數(shù)之間以&相連,且單個(gè)參數(shù)的鍵值間以=連接狡耻,并將參數(shù)以墩剖?開頭猴凹,經(jīng)過編碼再追加在url后面,而POST卻是將其放入請(qǐng)求體HTTPBody的岭皂。
另外一個(gè)HTTP連接的請(qǐng)求頭HTTPHeaderField設(shè)置也是非常重要的郊霎,它提供了很多字段,用于不同場(chǎng)景爷绘,不同特征下的使用书劝。

生成請(qǐng)求request的地方是在AFHTTPSessionManager中,當(dāng)時(shí)我們說了該類有名為序列化器的屬性requestSerializer土至,然后調(diào)用該序列化器的方法生成了一個(gè)request购对。當(dāng)時(shí),因?yàn)檫@是AFHTTPRequestSerializer類中的方法陶因,所以對(duì)于具體實(shí)現(xiàn)沒有研究骡苞。今天我們的工作就是這個(gè)。


源碼

先來看頭文件AFHTTPRequestSerializer.h坑赡。它繼承自NSObject烙如,實(shí)現(xiàn)了AFURLRequestSerialization協(xié)議么抗。

首先看頭文件中定義的屬性:


@property (nonatomic, assign) NSStringEncoding stringEncoding;

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

@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; // 緩存策略

@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;  // 是否處理Cookie

@property (nonatomic, assign) BOOL HTTPShouldUsePipelining; // 是否開啟管線化

@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

@property (nonatomic, assign) NSTimeInterval timeoutInterval;

@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;

@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;

stringEncoding意為序列化參數(shù)時(shí)的字符串編碼,默認(rèn)是NSUTF8StringEncoding蝇刀;
HTTPRequestHeaders是個(gè)字典螟加,意為即將被序列化的HTTP的請(qǐng)求頭。默認(rèn)包括Accept-LanguageUser-Agent等字段吞琐;
HTTPMethodsEncodingParametersInURI是個(gè)集合對(duì)象捆探,它表示序列化時(shí)參數(shù)被追加在url里的請(qǐng)求方式的集合,想想也知道站粟,它的元素應(yīng)該是GET黍图、Head
其他幾個(gè)屬性都比較好理解奴烙,而且注釋寫得很清楚了助被,不解釋了。

接著看在頭文件中暴露的前幾個(gè)方法:


+ (instancetype)serializer;

- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;

- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password;
- (void)clearAuthorizationHeader;

- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;

- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;

首先是初始化方法切诀,return一個(gè)該類的實(shí)例對(duì)象揩环;然后是兩個(gè)對(duì)請(qǐng)求頭字典操作的兩個(gè)方法;再接著是兩個(gè)關(guān)于登錄認(rèn)證的方法幅虑;然后是一個(gè)設(shè)置序列化類型的方法丰滑,代表遵循什么樣的規(guī)則進(jìn)行queryString轉(zhuǎn)換。參數(shù)是個(gè)枚舉倒庵,但是這個(gè)枚舉只有一個(gè)值AFHTTPRequestQueryStringDefaultStyle褒墨。最后一個(gè)方法提供了以block形式自定義queryString轉(zhuǎn)換的接口炫刷,也就是說可以通過block回調(diào)的方式讓調(diào)用者以自己的方式完成queryString的轉(zhuǎn)換。

最后就剩下三個(gè)核心方法了郁妈。其中第一個(gè)方法便是我們?cè)谇懊嬉呀?jīng)接觸過的柬唯,由HTTP methodURLString圃庭、parameters返回一個(gè)請(qǐng)求request锄奢。

下面代碼注釋非常占篇幅,但是注釋的很好剧腻,舍不得刪拘央。

Creates an NSMutableURLRequest object with the specified HTTP method and URL string.

If the HTTP method is GET, HEAD, or DELETE, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the parameterEncoding property, and set as the request body.

/**
 Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string.

 If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body.

 @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`.
 @param URLString The URL string used to create the request URL.
 @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body.
 @param error The error that occurred while constructing the request.

 @return An `NSMutableURLRequest` object.
 */
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(nullable id)parameters
                                     error:(NSError * _Nullable __autoreleasing *)error;

/**
 Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2

 Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream.

 @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`.
 @param URLString The URL string used to create the request URL.
 @param parameters The parameters to be encoded and set in the request HTTP body.
 @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
 @param error The error that occurred while constructing the request.

 @return An `NSMutableURLRequest` object
 */
- (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;

/**
 Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished.

 @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`.
 @param fileURL The file URL to write multipart form contents to.
 @param handler A handler block to execute.

 @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request.

 @see https://github.com/AFNetworking/AFNetworking/issues/1398
 */
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(nullable void (^)(NSError * _Nullable error))handler;

頭文件中基本就是以上東西,現(xiàn)在我們應(yīng)該如饑似渴书在,迫不及待的開始看.m文件了灰伟。

@interface AFHTTPRequestSerializer ()

@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;

@end

@implementation AFHTTPRequestSerializer

+ (instancetype)serializer {
    return [[self alloc] init];
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.stringEncoding = NSUTF8StringEncoding;

    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
    self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);

    // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
    NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
    [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        float q = 1.0f - (idx * 0.1f);
        [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
        *stop = q <= 0.5f;
    }];
    [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];

    NSString *userAgent = nil;
#if TARGET_OS_IOS
    // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
#elif TARGET_OS_WATCH
    // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
    userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
#endif
    if (userAgent) {
        if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
            NSMutableString *mutableUserAgent = [userAgent mutableCopy];
            if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                userAgent = mutableUserAgent;
            }
        }
        [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
    }

    // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
    self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];

    // 在AFHTTPRequestSerializer的初始化方法init中就初始化了集合mutableObservedChangedKeyPaths。并且遍歷AFHTTPRequestSerializerObservedKeyPaths數(shù)組儒旬,為每一項(xiàng)添加觀察
    self.mutableObservedChangedKeyPaths = [NSMutableSet set];
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }

    return self;
}

- (void)dealloc {
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext];
        }
    }
}

首先是初始化方法栏账,乍看這里感覺好復(fù)雜,其實(shí)一點(diǎn)不復(fù)雜栈源。初始化方法說到底就是初始化挡爵。它首先初始化了幾個(gè)屬性。stringEncoding屬性初始化默認(rèn)為NSUTF8StringEncoding甚垦;然后初始化了用于裝HTTP請(qǐng)求頭屬性的字典mutableHTTPRequestHeaders茶鹃;還初始化了修改請(qǐng)求頭時(shí)而專門創(chuàng)建的隊(duì)列requestHeaderModificationQueue
接下來是一大串亂糟糟的代碼艰亮,仔細(xì)看就明白了:此時(shí)既然已初始化了裝置請(qǐng)求頭屬性的字典闭翩,那不就可以先設(shè)置一些可以設(shè)置的請(qǐng)求頭屬性了。即Accept-LanguageUser-Agent迄埃。

初始化方法的最后為序列化需要觀察的屬性添加了監(jiān)聽疗韵。這里是指哪些需要觀察的屬性字段呢?從上面代碼可以看到侄非,它是由一個(gè)C函數(shù)獲取的AFHTTPRequestSerializerObservedKeyPaths()蕉汪,返回了一個(gè)數(shù)組,數(shù)組便是需要觀察的屬性字段的數(shù)組彩库。

而當(dāng)我們?cè)O(shè)置了這些HTTP配置屬性的值時(shí)肤无,就會(huì)觸發(fā)觀察回調(diào)的方法,在此方面里將該屬性字符串放入了mutableObservedChangedKeyPaths數(shù)組骇钦。代碼如下:

// 當(dāng)我們?cè)O(shè)置了這些HTTP配置屬性的值時(shí)宛渐,就會(huì)觸發(fā)觀察回調(diào)方法。并將該屬性字符串放入mutableObservedChangedKeyPaths集合
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

接著往下看:

- (NSDictionary *)HTTPRequestHeaders {
    NSDictionary __block *value;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
    });
    return value;
}

- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders setValue:value forKey:field];
    });
}

- (NSString *)valueForHTTPHeaderField:(NSString *)field {
    NSString __block *value;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        value = [self.mutableHTTPRequestHeaders valueForKey:field];
    });
    return value;
}

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password
{
    NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
    [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}

- (void)clearAuthorizationHeader {
    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
    });
}

#pragma mark -

- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {
    self.queryStringSerializationStyle = style;
    self.queryStringSerialization = nil;
}

- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block {
    self.queryStringSerialization = block;
}

上面這些代碼沒什么可說的,接著往下看窥翩,就到了最核心的方法了:

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

    /*
     為該HTTP的request設(shè)置配置屬性

     方法AFHTTPRequestSerializerObservedKeyPaths()返回一個(gè)數(shù)組业岁,代表我們需要關(guān)注的HTTP配置屬性。
     而mutableObservedChangedKeyPaths集合代表我們已設(shè)置的配置屬性寇蚊,
     */
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    /*
     將傳入的parameters進(jìn)行編碼笔时,添加到request中
     */
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    
    return mutableRequest;
}

開始閱讀該方法≌贪叮可以看到允耿,首先是對(duì)幾個(gè)參數(shù)的斷言。然后以URLString生成url扒怖,再以url生成mutableRequest较锡,并為其該HTTP請(qǐng)求設(shè)置配置屬性。然后它是將最核心的模塊封裝在了一個(gè)本類的方法里盗痒。我們跳入該方法蚂蕴,準(zhǔn)備繼續(xù)閱讀,此時(shí)發(fā)現(xiàn)不光本類中有這個(gè)方法俯邓,它的幾個(gè)子類中也實(shí)現(xiàn)了該方法骡楼。原來這個(gè)方法便是定義在AFURLRequestSerialization協(xié)議中的方法。在不同的子類中有不同的實(shí)現(xiàn)稽鞭,用以實(shí)現(xiàn)不同的功能鸟整。下面是本類中該方法的實(shí)現(xiàn):

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    /*
     1.為request設(shè)置請(qǐng)求頭
     */
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    
    
    /*
     2.參數(shù)序列化處理(將參數(shù)字典解析為url query)
     */
    NSString *query = nil;
    if (parameters) {
        // 若自定義了queryStringSerialization,那么就使用自定義的queryStringSerialization構(gòu)建方式
        if (self.queryStringSerialization)
        {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError); // 通過queryStringSerialization構(gòu)建query字符串

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        }
        else
        {
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters); // 由參數(shù)生成url的query部分
                    break;
            }
        }
    }

    // HTTPMethodsEncodingParametersInURI是個(gè)NSSet川慌,裝了"GET"吃嘿、"HEAD"祠乃、"DELETE"請(qǐng)求方式(在該類的初始化方法里初始化了)梦重,因?yàn)檫@幾個(gè)請(qǐng)求方式的query是直接拼接在url后面的。
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    }
    else // 若請(qǐng)求方式為"POST"亮瓷、"PUT"等琴拧,則需要將query設(shè)置到http body上
    {
        // #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]]; // 將query string編碼
    }

    return mutableRequest;
}

該方法里的脈絡(luò)很清晰,分為兩個(gè)步驟嘱支。第一步便是為request設(shè)置請(qǐng)求頭屬性蚓胸;第二步是參數(shù)序列化處理:先看是否實(shí)現(xiàn)自定義的序列化處理block,若有除师,則調(diào)用自定義的block沛膳,讓使用者自己實(shí)現(xiàn)參數(shù)序列化;若無汛聚,則調(diào)用AFQueryStringFromParameters()方法锹安,由參數(shù)字典轉(zhuǎn)換為query string。關(guān)于這個(gè)函數(shù)如何將字典解析為query string的釋義,這篇文章已經(jīng)解釋得很好:AFNetworking源碼閱讀(二)叹哭∪趟危總之,現(xiàn)在有了query string了风罩,但還是要根據(jù)HTTP method的不同糠排,將其放在正確的位置上。方法里接下來的代碼就是完成這部分的超升。

值得注意的是在HTTP method為POST等其他時(shí)入宦,它設(shè)置Content-Typeapplication/x-www-form-urlencoded,這個(gè)代表什么意思呢室琢?我趕緊百度了下云石。收集了以下干貨資料:
四種常見的 POST 提交數(shù)據(jù)方式
HTTP協(xié)議之multipart/form-data請(qǐng)求分析
關(guān)于 Content-Type:application/x-www-form-urlencoded 和 Content-Type:multipart/related


結(jié)尾

剛說到了Content-Type了,但時(shí)間不早了研乒,明天把這部分補(bǔ)充完整汹忠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市雹熬,隨后出現(xiàn)的幾起案子宽菜,更是在濱河造成了極大的恐慌,老刑警劉巖竿报,帶你破解...
    沈念sama閱讀 211,496評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铅乡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡烈菌,警方通過查閱死者的電腦和手機(jī)阵幸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芽世,“玉大人挚赊,你說我怎么就攤上這事〖闷埃” “怎么了荠割?”我有些...
    開封第一講書人閱讀 157,091評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長旺矾。 經(jīng)常有香客問我蔑鹦,道長,這世上最難降的妖魔是什么箕宙? 我笑而不...
    開封第一講書人閱讀 56,458評(píng)論 1 283
  • 正文 為了忘掉前任嚎朽,我火速辦了婚禮,結(jié)果婚禮上柬帕,老公的妹妹穿的比我還像新娘哟忍。我一直安慰自己室囊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,542評(píng)論 6 385
  • 文/花漫 我一把揭開白布魁索。 她就那樣靜靜地躺著融撞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粗蔚。 梳的紋絲不亂的頭發(fā)上尝偎,一...
    開封第一講書人閱讀 49,802評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音鹏控,去河邊找鬼致扯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛当辐,可吹牛的內(nèi)容都是我干的抖僵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,945評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼缘揪,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼耍群!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起找筝,我...
    開封第一講書人閱讀 37,709評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤蹈垢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后袖裕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曹抬,經(jīng)...
    沈念sama閱讀 44,158評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,502評(píng)論 2 327
  • 正文 我和宋清朗相戀三年急鳄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谤民。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,637評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疾宏,死狀恐怖张足,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灾锯,我是刑警寧澤兢榨,帶...
    沈念sama閱讀 34,300評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站顺饮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凌那。R本人自食惡果不足惜兼雄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,911評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帽蝶。 院中可真熱鬧赦肋,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,744評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至趣避,卻和暖如春庞呕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背程帕。 一陣腳步聲響...
    開封第一講書人閱讀 31,982評(píng)論 1 266
  • 我被黑心中介騙來泰國打工住练, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人愁拭。 一個(gè)月前我還...
    沈念sama閱讀 46,344評(píng)論 2 360
  • 正文 我出身青樓讲逛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親岭埠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盏混,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,500評(píng)論 2 348

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)惜论,斷路器括饶,智...
    卡卡羅2017閱讀 134,632評(píng)論 18 139
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 10,927評(píng)論 6 13
  • AFHTTPRequestOperationManager 網(wǎng)絡(luò)傳輸協(xié)議UDP、TCP来涨、Http图焰、Socket、X...
    Carden閱讀 4,326評(píng)論 0 12
  • JAVA序列化機(jī)制的深入研究 對(duì)象序列化的最主要的用處就是在傳遞,和保存對(duì)象(object)的時(shí)候,保證對(duì)象的完整...
    時(shí)待吾閱讀 10,842評(píng)論 0 24
  • 我終于遞交了辭呈蹦掐,把公司里的私人物品全都?xì)w納在一個(gè)小小的灰色的手提箱里技羔,像被遺棄了的小狗,茫然不知?dú)w屬卧抗。 我成了待...
    _微微閱讀 356評(píng)論 0 1