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,其子类有AFJSONResponseSerializerAFXMLDocumentResponseSerializerAFPropertyListResponseSerializer

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

这个方法中根据 acceptableContentTypesacceptab;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请求进行处理

  1. 处理查询的URL参数
  2. 设置HTTP头部字段
  3. 设置请求的属性
  4. 分块上传

处理查询参数

处理查询参数组要通过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])];
    }
}

使用AFPercentEscapedStringFromStringC函数来对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-LanguageUser-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;
}

这个方法完成了一下几件事

  1. 对参数进行检查
  2. 设置HTTP方法
  3. 通过mutableObservedChangedKeyPaths设置NSMutableRequest的属性
  4. 调用了[AFHTTPRequestSerializer requestBySerializingRequest:withParameters:error:]方法来设置HTTP头部字段和查询参数

    在这个方法中做了两件事

    1. 设置HTTP头部
    2. 调用AFQueryStringFromParameters将参数转为查询参数
    3. 将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;
      }
  5. 最后这方法会返回一个NSMutableURLRequest

AFPropertyListRequestSerializer AFJSONRequestSerializer

AFJSONRequestSerializerAFPropertyListRequestSerializer
均继承自AFHTTPRequestSerializer

其重写了- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error方法,主要内部修改了contentType和设置的httpBody的数据格式化

我们只需要在请求时设置manager 的 requestSerializer为[AFJSONRequestSerializer serializer]