AFURLRequestSerialization
主要是對請求進行編碼.
1. AFURLRequestSerialization協議
AFURLRequestSerialization
是一個協議, 請求序列化將引數編碼為查詢字串、HTTP主體、必要時設定適當的HTTP頭欄位.
AFURLRequestSerialization
協議中宣告瞭一個方法:
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
複製程式碼
根據指定的引數parameters
將request
進行編碼, 並將編碼以後的request
進行返回.
AFURLRequestSerialization
檔案中宣告瞭三種序列化器, 分別為:
AFHTTPRequestSerializer
實現了AFURLRequestSerialization協議, 查詢字串/URL表單編碼的引數序列化和預設的請求頭,以及響應狀態程式碼和內容型別驗證.
AFJSONRequestSerializer
是AFHTTPRequestSerializer
的一個子類, 將parameters
引數使用NSJSONSerialization序列化為JSON, 並且設定Content-Type
為application/json
.
AFPropertyListRequestSerializer
是AFHTTPRequestSerializer
的一個子類, 將parameters
引數使用NSPropertyListSerializer序列化為JSON, 並且設定Content-Type
為application/x-plist
.
2. 對外提供的序列化函式
FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);
複製程式碼
個人理解為對外提供了兩個全域性函式
AFPercentEscapedStringFromString
:將指定的string字串進行百分號編碼.
AFQueryStringFromParameters
:將指定的parameters
字典轉換為查詢字串,
3. 在AFN中的使用
我們來看一下AFN
是如何使用AFHTTPRequestSerializer
進行編碼的, 在AFHTTPSessionManager
的dataTaskWithHTTPMethod
方法中進行構建NSMutableURLRequest
的方法如下.
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
......
}
複製程式碼
self.requestSerializer
如果沒有特殊設定這裡預設的是AFHTTPRequestSerializer, 所以這裡我們看一下AFHTTPRequestSerializer的序列化方法.
4. AFHTTPRequestSerializer構建
- (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];
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;
}
複製程式碼
AFHTTPRequestSerializer的建立也是很重要的一部分.
stringEncoding
指定預設的編碼方式為UTF8編碼.
mutableHTTPRequestHeaders
儲存了我們修改的請求頭的資訊, 當對請求頭做修改時會在requestHeaderModificationQueue
並行佇列中執行, 將修改的資訊儲存在mutableHTTPRequestHeaders
字典中, 在呼叫requestBySerializingRequest
對請求編碼的時候遍歷這個字典, 給request設定header.
acceptLanguagesComponents
表示客戶端支援的語言.
userAgent
將客戶端的環境通過User-Agent
欄位傳給伺服器.
通過AFHTTPRequestSerializerObservedKeyPaths
函式獲取AFN監聽哪些頭部欄位的變化, 並且提供監聽方法, 在監聽方法中如果有值改變, 就賦值給mutableHTTPRequestHeaders
字典.
5. AFURLRequestSerialization序列化方法
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 從自己的head遍歷, 如果有值就給request的header賦值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 把各種型別的引數, 轉成string
NSString *query = nil;
if (parameters) {
// 按照自定義的方式解析
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
// 預設的方式解析引數
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
// 將傳入的paramter引數, 用=號連結(key1=name1&key2=name2)
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// HTTPMethodsEncodingParametersInURI, 預設為`GET`, `HEAD`, and `DELETE`
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
// 拼接URL
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;
}
複製程式碼
將傳入的parameters引數序列化到request中.
先遍歷HTTPRequestHeaders
字典, 將遍歷到的值設定到request請求頭中.
如果有自定的queryStringSerialization
序列化方式, 就執行自定義, 否則按照預設的方式序列化. 這裡呼叫了AFN的自定義函式AFQueryStringFromParameters
進行序列化, 下邊會講解這個函式.
比如我們傳進來的引數是@{@"key1" : @"name1", @"key2" : @"name2", @"key3" : @"哈哈"}
, 引數會經過百分號編碼, 執行AFQueryStringFromParameters
序列化之後是key1=name1&key2=name2&key3=%E5%93%88%E5%93%88
.
如果request的HTTPMethod是GET, HEAD或者DELETE, 就把經過序列化以後的查詢字串拼接到request的URL中. 否則將查詢字串query設定到request的請求體中, 至此request請求設定完畢.
6.AFURLRequestSerialization中的輔助函式
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);
}
/*{
@"key1" : @"value1",
@"key2" : @"value2"
}*/
/**
對引數進行轉碼,
*/
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) {
[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;
}
複製程式碼