AFURLSerialization
在对请求和接收响应的过程中有两个序列化的模块:
- AFURLRequestSerialization
- AFURLResponseSerialization
AFURLRequestSerialization
主要用于修改请求(主要是HTTP请求)的头部,提供了一些语义明确的接口设置HTTP头部字段
AFURLResponseSerialization
将请求返回的数据解析为对应的格式
AFURLResponseSerialization
AFURLResponseSerialization
其实只是一个协议
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
当遵循了这个协议类的同时也要遵循NSObject、NSSecureCoding、NSCopying,用以实现安全编码、拷贝等OC对象的基本行为
该模块中的父类为AFHTTPResponseSerializer
并且继承了AFURLResponseSerialization
,其子类有AFJSONResponseSerializer
、AFXMLDocumentResponseSerializer
、AFPropertyListResponseSerializer
等
AFHTTPResponseSerializer
这是该模块中的父类 也是最重要的类
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;
return self;
}
初始化 设置acceptableStatusCodes
接受的状态码为200-299
只有在这个范围内 才表示获取了有效响应
验证有效性
在AFHTTPResponseSerializer
最重要的方法就是- [AFHTTPResponseSerializer validateResponse:data:error:]
- (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]] &&
!([response MIMEType] == nil && [data length] == 0)) {
...
//返回的类型 contentType无效
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
...
//返回的状态码无效
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
这个方法中根据 acceptableContentTypes
和acceptab;leStatusCodes
来判断当前响应是否有效
通过AFErrorWithUnderlyingError
生成格式化的错误
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
协议实现
对于协议只是简单的调用了上面的对返回数据做了验证
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
AFJSONResponseSerializer
AFJSONResponseSerializer
继承自AFHTTPResponseSerializer
初始化
- (instancetype)init {
...
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
AFJSONResponseSerializer *serializer = [[self alloc] init];
serializer.readingOptions = readingOptions;
return serializer;
}
设置了acceptableContentTypes
属性
重写协议实现
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
#1: 验证请求
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#2: 解决一个由只包含一个空格的响应引起的 bug, 略
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) {
return nil;
}
#3: 序列化 JSON
NSError *serializationError = nil;
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
#4: 移除 JSON 中的 null
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
其中移除JSON中null的函数是一个递归调用的函数
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
//移除value为null 的键值对
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
AFURLRequestSerialization
AFURLRequestSerialization
主要工作是对发出的HTTP请求进行处理
- 处理查询的URL参数
- 设置HTTP头部字段
- 设置请求的属性
- 分块上传
处理查询参数
处理查询参数组要通过AFQueryStringPair
类,以及一些C函数共同完成。
AFQueryStringPair
中存储了两个属性
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
对应HTTP请求中查询URL中的参数
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
使用AFPercentEscapedStringFromString
C函数来对filed和value进行处理,使用URL形式,将特殊字符转为百分号表示形式。返回key=value
这种形式
使用C函数AFQueryStringPairsFromKeyAndValue
将参数字典转为AFQueryStringPair
对象数组,然后使用AFQueryStringFromParameters
用&拼接参数,返回username=dravenss&password=123456&hello[world]=helloworld
形式
设置HTTP头部字段
AFHTTPRequestSerializer
帮助我们设置HTTP请求头,在内部提供了- (void)setValue:(NSString *)valueforHTTPHeaderField:(NSString *)field
方法来设置HTTP头部,它的实现基于mutableHTTPRequestHeaders
属性
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
当我们设置请求头时都会设置到mutableHTTPRequestHeaders
这个可变字典中,当真正使用时,通过HTTPRequestHeaders
方法来获取对应版本的不可变字典
- (NSDictionary *)HTTPRequestHeaders {
NSDictionary __block *value;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
});
return value;
}
AFHTTPRequestSerializer设置常用的HTTP头
在初始化时,根据编译平台 设置了userAgent
字符串
#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]];
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"];
}
设置账号 密码等验证字段
- (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"];
});
}
设置请求的属性
AFNetworking
提供了这些属性来设置
@property (nonatomic, assign) BOOL allowsCellularAccess;
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
存储在数组中
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;
}
当这些属性被设置时 会触发KVO,将新的属性存储在一个名字为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];
}
}
}
然后在生成request时 设置这些属性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
流程
AFHTTPRequestSerializer
会在Manager
初始化之后进行一些初始化,它会根据当前系统环境预设置一些HTTP头部字段Accept-Language
、User-Agent
等
在完成了HTTP 请求头和属性的设置后,
在调用请求的方法时,会调用到序列化的这个方法
- (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;
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
这个方法完成了一下几件事
- 对参数进行检查
- 设置HTTP方法
- 通过
mutableObservedChangedKeyPaths
设置NSMutableRequest
的属性 调用了
[AFHTTPRequestSerializer requestBySerializingRequest:withParameters:error:]
方法来设置HTTP头部字段和查询参数在这个方法中做了两件事
- 设置HTTP头部
- 调用
AFQueryStringFromParameters
将参数转为查询参数 将params添加到url或者http body中
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
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) {
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:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
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 {
// #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;
}
最后这方法会返回一个
NSMutableURLRequest
AFPropertyListRequestSerializer AFJSONRequestSerializer
AFJSONRequestSerializer
、 AFPropertyListRequestSerializer
均继承自AFHTTPRequestSerializer
其重写了- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error
方法,主要内部修改了contentType和设置的httpBody的数据格式化
我们只需要在请求时设置manager 的 requestSerializer为[AFJSONRequestSerializer serializer]