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;}复制代码