Serialization 包括 AFURLResponseSerialization
和 AFURLRequestSerialization
AFURLResponseSerialization 是处理响应的模块,将请求返回的数据解析成对应的格式。而AFURLRequestSerialization的主要作用是修改请求(主要是 HTTP 请求)的头部,提供了一些语义明确的接口设置 HTTP 头部字段。
AFURLResponseSerialization
AFURLResponseSerialization 是一个协议,协议的内容为:
1 2 3
| - (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response data:(nullable NSData *)data error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
|
它的实现类为:
- AFURLResponseSerialization
- AFHTTPResponseSerializer
- AFJSONResponseSerializer
- AFXMLParserResponseSerializer
- AFXMLDocumentResponseSerializer
- AFPropertyListResponseSerializer
- AFImageResponseSerializer
- AFCompoundResponseSerializer
在文件中 所有类都遵循AFURLResponseSerialization,而AFHTTPResponseSerializer是其他类的基类。
AFHTTPResponseSerializer
初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| + (instancetype)serializer { return [[self alloc] init]; }
- (instancetype)init { self = [super init]; if (!self) { return nil; }
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.acceptableContentTypes = nil;
return self; }
|
因为是对 HTTP 响应进行序列化,所以这里设置了 stringEncoding 为 NSUTF8StringEncoding 而且没有对接收的内容类型加以限制。
将 acceptableStatusCodes 设置为从 200 到 299 之间的状态码, 因为只有这些状态码表示获得了有效的响应。
验证响应的有效性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| - (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error { BOOL responseIsValid = YES; NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { #1: 返回内容类型无效 }
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { #2: 返回状态码无效 } }
if (error && !responseIsValid) { *error = validationError; }
return responseIsValid; }
|
这个方法根据在初始化方法中初始化的属性 acceptableContentTypes 和 acceptableStatusCodes 来判断当前响应是否有效。
AFURLResponseSerialization协议的实现
1 2 3 4 5 6 7 8
| - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data; }
|
调用上面的方法对响应进行验证,然后返回数据
AFJSONResponseSerializer
初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| + (instancetype)serializer { return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; }
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { AFJSONResponseSerializer *serializer = [[self alloc] init]; serializer.readingOptions = readingOptions;
return serializer; }
- (instancetype)init { self = [super init]; if (!self) { return nil; }
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self; }
|
相对AFHTTPResponseSerializer 设置了acceptableContentTypes;
AFURLResponseSerialization协议的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { 1. 验证请求
2. 解决一个由只包含一个空格的响应引起的 bug
3. 序列化 JSON 4. 移除 JSON 中的 null
if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); }
return responseObject; }
|
1. 验证请求
1 2 3 4 5
| if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { return nil; } }
|
调用自身的validateResponse验证响应,
validateResponse:中如果content-type不满足,那么产生的validationError就是Domain为AFURLResponseSerializationErrorDomain,code为NSURLErrorCannotDecodeContentData,再用 AFErrorOrUnderlyingErrorHasCodeInDomain检验error 和domain,如果就是这两个值 就返回nil。
2. 解决一个由只包含一个空格的响应引起的 bug
1 2 3 4 5
| BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) { return nil; }
|
对于’head :ok’,Rails返回的是一个空格 (这是Safari上的一个bug),并且这样的JSON格式不会被NSJSONSerialization解析。如果是单个空格,就不解析
查看 Issues 1742
3.序列化 JSON
1 2 3 4 5 6 7 8 9 10 11
| NSError *serializationError = nil;
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject) { if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); } return nil; }
|
使用系统自带的NSJSONSerialization来解析NSData数据,如果serializationError不为空,那么最终的error其实就是serializationError
4. 移除 JSON 中的 null
1 2 3
| if (self.removesKeysWithNullValues) { return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); }
|
AFJSONObjectByRemovingKeysWithNullValues通过递归的方法,把JSON中NSDictionary的数据(不包括NSArray)中的对应value为空的key移除。
AFURLRequestSerialization
AFURLRequestSerialization 的主要工作是对发出的 HTTP 请求进行处理。
使用
1 2 3 4 5
| [[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil];
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil];
[[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil];
|
AFURLRequestSerialization
AFURLRequestSerialization 是一个协议
1 2 3 4
| - (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
|
它的实现类为:
- AFURLRequestSerialization
- AFHTTPRequestSerializer
- AFJSONRequestSerializer
- AFPropertyListRequestSerializer
AFHTTPRequestSerializer是所有类的基类,AFJSONRequestSerializer 和 AFPropertyListRequestSerializer继承了AFHTTPRequestSerializer。
AFHTTPRequestSerializer
初始化:
1 2 3 4 5 6 7 8 9
| + (instancetype)serializer { return [[self alloc] init]; }
- (instancetype)init{ 1. 初始化参数, 2. 设置HTTP 头部字段 3. KVO }
|
1. 初始化参数
1 2 3 4 5 6 7 8
| self.stringEncoding = NSUTF8StringEncoding; self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; self.mutableObservedChangedKeyPaths = [NSMutableSet set];
|
初始化了一些参数 ,HTTPMethodsEncodingParametersInURI表示需要将的quey是拼接到url后面的的方法,就是GET HEAD DELETE
,与此对应的POST PUT
方法将query拼到http body中。mutableHTTPRequestHeaders 储存http头部信息。mutableObservedChangedKeyPaths保存需要键值观察的值。
requestHeaderModificationQueue ??
2. 设置HTTP 头部字段
1 2 3 4 5 6 7
| NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; ... [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; NSString *userAgent = nil; ... [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
|
分别设置了acceptLanguagesComponents和userAgent信息。
其中acceptLanguagesComponents
一般为:
userAgent为:
1
| [工程名字]/[工程版本] (iPhone; iOS 10.2; Scale/2.00)
|
3.KVO
1 2 3 4 5
| for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; } }
|
对一些属性进行 KVO,确保它们在改变后更新 NSMutableURLRequest 中对应的属性。其中AFHTTPRequestSerializerObservedKeyPaths为这些值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| //是否允许使用设备的蜂窝移动网络来创建request,默认为允许 allowsCellularAccess, /* 创建的request所使用的缓存策略,默认使用`NSURLRequestUseProtocolCachePolicy`,该策略表示 如果缓存不存在,直接从服务端获取。如果缓存存在,会根据response中的Cache-Control字段判断 下一步操作,如: Cache-Control字段为must-revalidata, 则 询问服务端该数据是否有更新,无更新话 直接返回给用户缓存数据,若已更新,则请求服务端.*/ cachePolicy, /** 如果设置HTTPShouldHandleCookies为YES,就处理存储在NSHTTPCookieStore中的cookies HTTPShouldHandleCookies表示是否应该给request设置cookie并随request一起发送出去 */ HTTPShouldHandleCookies, /** HTTPShouldUsePipelining表示receiver(理解为iOS客户端)的下一个信息是否必须等到上一个请求回复才能发送。 如果为YES表示可以,NO表示必须等receiver收到先前的回复才能发送下个信息。 */ HTTPShouldUsePipelining, /** 设定request的network service类型. 默认是`NSURLNetworkServiceTypeDefault`. 这个network service是为了告诉系统网络层这个request使用的目的 比如NSURLNetworkServiceTypeVoIP表示的就这个request是用来请求网际协议通话技术(Voice over IP)。
系统能根据提供的信息来优化网络处理,从而优化电池寿命,网络性能等等 */ networkServiceType, /** 超时机制,默认60秒 */ timeoutInterval
|
AFURLRequestSerialization 协议的实现
初始化之后,如果调用了 - [AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:],就会进入 AFHTTPRequestSerializer 的这一方法:
1 2 3 4
| - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error
|
其中调用了AFURLRequestSerialization 协议的实现:
1 2 3 4 5 6 7 8 9 10
| - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error{ 1. 通过 HTTPRequestHeaders 字典设置头部字段 2. 将parameters转换为查询参数query 3. 将 query 添加到 URL 或者 HTTP body 中
}
|
1.通过 HTTPRequestHeaders 字典设置头部字段
1 2 3 4 5
| [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }];
|
2. 将parameters转换为查询参数query
1
| query = AFQueryStringFromParameters(parameters);
|
AFQueryStringFromParameters (调用)-> AFQueryStringPairsFromDictionary -> AFQueryStringPairsFromKeyAndValue
其中AFQueryStringPairsFromKeyAndValue递归调用并解析,返回查询参数。
3. 将 query 添加到 URL 或者 HTTP body 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 { if (!query) { query = @""; } if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; }
|
最后这个方法会返回一个 NSMutableURLRequest。