源码解读IOS

AFNetWorking 源码学习笔记 ☞ Serializat

2018-11-28  本文已影响27人  RiverSea
AFNetWorking 源码学习笔记.png

一、前言

本文是 AFNetWorking 源码学习笔记的第四篇,将介绍第三部分 -- Serialization 目录下的文件:AFURLRequestSerialization(.h/.m) 和 AFURLResponseSerialization(.h/.m) ,仔细观察这些文件发现,AFURLRequestSerialization 和 AFURLResponseSerialization 不仅是文件名,还是 2 个协议的名称。

AFNetWorking-Serialization.png

二、正文

1. AFURLRequestSerialization(.h/.m)

先看 AFURLRequestSerialization.h 文件,这个文件里边共有 2 个协议和 3 个类,他们之间的关系如下:

AFURLRequestSerialization-Simple.png

这里,我们以 上一篇 提到的构造 request 的 2 个方法为主线开始介绍。

// 方法一

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method
                                                               URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
                                                              parameters:parameters
                                                                   error:&serializationError];
// 方法二
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" 
                                                                            URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] 
                                                                           parameters:parameters 
                                                            constructingBodyWithBlock:block 
                                                                                error:&serializationError];

先来看看第一类请求方法的实现中构建 MutableURLRequest 用到的方法:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    // 0.检测 method、URLString 是否为空
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];
    NSParameterAssert(url);    

    // 1.开始创建(系统方法创建)
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.设置参数
    // 2.1 请求方法
    mutableRequest.HTTPMethod = method; // 方法默认是 “GET”

    // 2.2 为 request 设置其一些默认参数
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        
        // AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6个属性对应 getter 方法名 string 组成的数组
        // self.mutableObservedChangedKeyPaths 中是 值非nil 的属性名
        // requstSerializer 初始化时,创建了 self.mutableObservedChangedKeyPaths 这个空 mutableSet,然后,如果设置了对应的参数,它里边就会加上对应的属性名
        
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    // 3.对参数进行序列化,并赋值给 request
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

该方法来自 AFHTTPRequestSerializer,具体功能见上边的代码注释,简单看一下部分细节,2.2 for 循环的代码块,其中 AFHTTPRequestSerializerObservedKeyPaths() 是一个静态函数,实际返回了当前类 AFHTTPRequestSerializer 中的几个重要的属性名对应的字符串数组,也可以认为是其 get 方法名对应的字符串数组。

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

类的初始化方法里边主要设置了一些 header,然后就是给上边方法提到的几个属性添加了 KVO 监听,当用户给这些属性赋值 (可以为nil)时,就会给 self.mutableObservedChangedKeyPaths 这个 mutableSet 增删元素 (相关代码如下):

// 1. init 方法中给属性添加监听的代码
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }

// 2. 触发的监听方法
- (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];
        }
    }
}

回到 for 循环中的语句,里边 if 语句的目的是将用户设置的属性值赋给 mutableRequest。

然后,来到 3. 序列化参数的时候,用到了这样一个方法 requestBySerializingRequest: withParameters: error:,它属于 AFURLRequestSerialization 这个协议,AFURLRequestSerializer 遵守协议并实现了该方法,注释见下方法注释。

#pragma mark - AFURLRequestSerialization

// 为 request 设置参数
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    // 0.将 request 转成 mutable,以便修改
    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    // 1.设置 header 字段,self.HTTPRequestHeaders 是在 init 方法中设置的 useragent 等参数
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        // 将 self.HTTPRequestHeaders 中的 header 参数设置到 request 里边
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    // 2.参数序列化
    NSString *query = nil;
    if (parameters) {
        // 2.1 self.queryStringSerialization 是一个 block,如果从外边 设置了序列化规则
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
         // 2.2 使用AF提供的序列化规则,将 string、array、dictionary 的 parameters 统一转化成了 key1=value1&key2=value2&key3=value3 格式的字符串
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    // 3.设置 request 的 URL 或 HTTPBody 👇
    
    // 初始化时已经设置了 self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
    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 { // POST 等其他方法,将 query 设置成 body
        
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        // 如果设置了 body,需要有 Content-Type
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        // self.stringEncoding 编码方式
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}

至此,第一种创建 mutableRequest 的主要方法就介绍完了,在讨论第二种创建 MutableURLRequest 的方法实现之前,我们先看看 AFNetWorking 中给出的一个上传数据的示例:

// AFNetWorking 中提供的示例
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST"
                                                                                          URLString:@"http://example.com/upload"
                                                                                         parameters:nil
                                                                          constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
    {
        [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"]
                                   name:@"file"
                               fileName:@"filename.jpg"
                               mimeType:@"image/jpeg"
                                  error:nil];
    } error:nil];

关注一下 constructingBodyWithBlock 中的代码就行,其中调用了参数 formData 遵守的一个协议方法 appendPartWithFileURL: ...。下面开始查看创建 MutableURLRequest 的方法实现:

- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(NSDictionary *)parameters
                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError *__autoreleasing *)error
{
    // 0.过滤不符合要求的情况
    NSParameterAssert(method);
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

    // 1.创建 mutableRequest
    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

    // 2.构建 formData
    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

    if (parameters) {
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            NSData *data = nil;
            if ([pair.value isKindOfClass:[NSData class]]) {
                data = pair.value;
            } else if ([pair.value isEqual:[NSNull null]]) {
                data = [NSData data];
            } else {
                data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
            }

            if (data) {
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }

    // 3.执行外部传入的 block
    if (block) {
        block(formData);
    }

    // 4.返回最终处理完的 request
    return [formData requestByFinalizingMultipartFormData];
}

大概内容见上方代码注释,下面我们讨论一下细节部分。

1.创建 mutableRequest 的方法实现及注释如下:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    // 0.检测 method、URLString 是否为空
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];
    NSParameterAssert(url);    

    // 1.开始创建(系统方法创建)
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.设置参数
    // 2.1 请求方法
    mutableRequest.HTTPMethod = method; // 方法默认是 “GET”

    // 2.2 为 request 设置其一些默认参数
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        
        // AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6个属性对应 getter 方法名 string 组成的数组
        // self.mutableObservedChangedKeyPaths 中是 值非nil 的属性名
        // requstSerializer 初始化时,创建了 self.mutableObservedChangedKeyPaths 这个空 mutableSet,然后,如果设置了对应的参数,它里边就会加上对应的属性名
        
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    // 3.对参数进行序列化,与第一种方法调用的是相同的方法,不过多介绍
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

2.构建 AFStreamingMultipartFormData * 类型的 formData,并为其添加数据。下面是它的几个重要方法:

AFStreamingMultipartFormData 是一个用于给 request 设置数据的类。通过初始化方法 initWithURLRequest: stringEncoding: ,将 request 赋值给 AFStreamingMultipartFormData 内部的 copy 属性 request,用于后边的处理及最终返回,。

formData 遵守协议 AFMultipartFormData,当然要实现其中的一些协议方法了,几个重要的方法 (协议方法和非协议方法) 实现见下方代码片段。

// AFStreamingMultipartFormData : NSObject <AFMultipartFormData>

- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
                    stringEncoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (!self) {
        return nil;
    }

    self.request = urlRequest;
    self.stringEncoding = encoding;
    self.boundary = AFCreateMultipartFormBoundary();
    self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];

    return self;
}

- (void)appendPartWithFormData:(NSData *)data
                          name:(NSString *)name
{
    NSParameterAssert(name);

    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];

    [self appendPartWithHeaders:mutableHeaders body:data];
}

- (void)appendPartWithHeaders:(NSDictionary *)headers
                         body:(NSData *)body
{
    NSParameterAssert(body);

    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = headers;
    bodyPart.boundary = self.boundary;
    bodyPart.bodyContentLength = [body length];
    bodyPart.body = body;

    [self.bodyStream appendHTTPBodyPart:bodyPart];
}

- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
    if ([self.bodyStream isEmpty]) {
        return self.request;
    }

    // Reset the initial and final boundaries to ensure correct Content-Length
    [self.bodyStream setInitialAndFinalBoundaries];
    [self.request setHTTPBodyStream:self.bodyStream];

    [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
    [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];

    return self.request;
}

我们发现,其中用到了 AFMultipartBodyStream 和 AFHTTPBodyPart 这 2 个类,其中 AFMultipartBodyStream 继承自 NSInputStream,将会作为参数传给 request。而 AFHTTPBodyPart 用于构造分段数据,并传递给 AFMultipartBodyStream。下边是 2 者的部分方法实现:

// AFMultipartBodyStream : NSInputStream <NSStreamDelegate>

@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;

// 添加 part
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
    [self.HTTPBodyParts addObject:bodyPart];
}

// 对所有的 part 组成的数组进行处理:第一个 part 的 hasInitialBoundary = YES,最后一个 part 的 hasFinalBoundary = YES,其他 part 的这两个属性s都是 NO。
// 这个在计算分段大小的时候会用到
- (void)setInitialAndFinalBoundaries {

    if ([self.HTTPBodyParts count] > 0) {

        for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
            bodyPart.hasInitialBoundary = NO;
            bodyPart.hasFinalBoundary = NO;
        }

        [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
    }
}

// 计算所有分段加起来的总数据
- (unsigned long long)contentLength {
    unsigned long long length = 0;
    for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
        length += [bodyPart contentLength];
    }

    return length;
}
// AFHTTPBodyPart : NSObject

// 计算每一个分段的大小
- (unsigned long long)contentLength {
    unsigned long long length = 0;

    // hasInitialBoundary 是前边 AFMultipartBodyStream 里的 setInitialAndFinalBoundaries 方法里边设置过的哦
    NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
    length += [encapsulationBoundaryData length];

    NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
    length += [headersData length];

    length += _bodyContentLength;

    // hasFinalBoundary 也是前边设置过的
    NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
    length += [closingBoundaryData length];

    return length;
}

3.执行 block ,其实也是在给 formData 添加数据,block 的实现中必须调用 AFMultipartFormData 中的协议方法。

4.返回结果时,调用了 formData 自己的方法 requestByFinalizingMultipartFormData(非协议方法),即为 request 的相关字段赋值,并将其返回。

最后,我们以这些类之间的关系图最为本部分的小结:

AFURLRequestSerialization.png

2. AFURLResponseSerialization

如前文所述,AFURLResponseSerialization 即是协议名称,又是文件名。之前在讨论 NSURLSessionTaskDelegate 的代理方法 - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error; 时有这样一行代码:

// AFURLSessionManagerTaskDelegate(Class)

// *** 使用 responseSerializer 处理返回结果
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

// AFURLSessionManager(Class)
self.responseSerializer = [AFJSONResponseSerializer serializer];

我们知道,这里的 manager 指的是 AFURLSessionManager,在其 init() 方法中初始化了一个重要属性 responseSerializer。那么 AFURLResponseSerialization 这个文件里边到底有哪些类,他们之间的关系又是怎样的?

AFURLResponseSerialization.png

从上图可以发现,这里有一个重要的基类 AFHTTPResponseSerializer,他遵守 AFURLResponseSerialization 这个协议 (只有一个协议方法:- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error;),其它几个类都是他的子类,他们都各自实现了该协议方法 ,用来处理接口返回的数据。

下面我们从基类 AFHTTPResponseSerializer 开始讨论,下面是它的头文件。

@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>

// 编码方式,不过源码中已经指明 It is never used。
@property (nonatomic, assign) NSStringEncoding stringEncoding;
// 可接受的 HTTP 状态码
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
// 可接受的 MIME types
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;


// 创建及初始化方法,创建默认配置的 serializer
- (instancetype)init;
+ (instancetype)serializer;

// 校验接口返回的指定 response 和 data,默认检查了 acceptable status code 和 content type
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
                    data:(nullable NSData *)data
                   error:(NSError * _Nullable __autoreleasing *)error;

@end

上边 3 个属性中的第 1 个 stringEncoding 已经被标记为过期,即不再对接收到的数据进行解码 (decode)。acceptableStatusCodes 和 acceptableContentTypes 是两个集合(set),依次表示客户端可以接受的响应报文的 HTTP 状态码和 Content-Type,用于后边对响应报文 response 和 data 进行校验。

再来看创建及初始化方法的实现,实际重点在 init 方法中,

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

即给 acceptableStatusCodes 和 acceptableContentTypes 赋初值,前者默认为从 200 到 299 之间的整数,及成功的状态码,后者默认为 nil,也就是说接收任何类型 (Content-Type) 的响应数据。

现在来看开头提的解析数据的方法:- (id)responseObjectForResponse: data: error:

#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

    return data;
}

这其实是 AFURLResponseSerialization 规定的协议方法,首先校验了返回的 response 和 data,如果出错,就将错误信息写入 error。最后将 data 返回。用于校验的方法实现如下:

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;
    NSError *validationError = nil;

    // 1.response 是 HTTP 响应报文的情况
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        
        // 2.ContentType 不符的情况
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {

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

            responseIsValid = NO;
        }

        // 2.StatusCode 不符的情况
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];

            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }

            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);

            responseIsValid = NO;
        }
    }

    // 3.保存错误信息-有错误信息并且response不符合要求的情况
    if (error && !responseIsValid) {
        *error = validationError;
    }

    // 4.返回
    return responseIsValid;
}

代码看起来有点长,实际做的事情比较少,首先判断响应的报文是否是 HTTP 的响应报文,如果不是,直接返回 responseIsValid (此时为 YES),如果是 HTTP 报文时才会进行接下来的判断。接着分别判断 StatusCode 和 ContentType 是否符合要求,如果不符合要求,则构建相应的 validationError 信息,并将 responseIsValid 置为 NO。

当 error 存在,并且 responseIsValid 为 NO 时,将 validationError 赋值给 error。最后返回校验结果。

至于 AFHTTPResponseSerializer 的子类,我们就只看一下 AFJSONResponseSerializer,其他子类与之类似,就不多做介绍了。

AFJSONResponseSerializer 是专门用于解析 JSON 格式的响应数据的。他重写了父类的初始化方法,给 acceptableContentTypes 添加了 application/json、text/json、text/javascript 这 3 中 json 相关的格式。

self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

同时通过两个工厂方法为属性 readingOptions 赋初值:

+ (instancetype)serializer {
    return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
}

+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
    AFJSONResponseSerializer *serializer = [[self alloc] init];
    serializer.readingOptions = readingOptions;

    return serializer;
}

最后,重写了解析数据的方法 (如下),先是和父类一样校验了 ContentType 和 StatusCode,然后反序列化拿到的响应数据,过滤掉反序列化后的数据中的 value 为 Null 的数据,最后将处理完的数据返回。

#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    // 1.调用父类方法,校验 ContentType 和 statusCode
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
            return nil;
        }
    }

    // 异常处理
    // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
    // See https://github.com/rails/rails/issues/1742
    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
    if (data.length == 0 || isSpace) {
        return nil;
    }
    
    NSError *serializationError = nil;
    
    // 2.反序列化数据
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
    if (!responseObject)
    {
        if (error) {
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    
    // 3.移除 value 为 Null 的 键值对
    if (self.removesKeysWithNullValues) {
        return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    }

    return responseObject;
}

以上就是构建及序列化请求参数的 AFHTTPRequestSerializer 和 处理响应数据的 AFHTTPResponseSerializer 的主要实现代码,当然还有很多细节,在此就不再多做介绍了。

参考

https://blog.csdn.net/tsunamier/article/details/53611811
https://blog.csdn.net/tsunamier/article/details/53673751

上一篇下一篇

猜你喜欢

热点阅读