AFNetWorking 源碼學(xué)習(xí)筆記 ? Serialization

AFNetWorking 源碼學(xué)習(xí)筆記.png

一、前言

本文是 AFNetWorking 源碼學(xué)習(xí)筆記的第四篇,將介紹第三部分 -- Serialization 目錄下的文件:AFURLRequestSerialization(.h/.m) 和 AFURLResponseSerialization(.h/.m) ,仔細(xì)觀察這些文件發(fā)現(xiàn),AFURLRequestSerialization 和 AFURLResponseSerialization 不僅是文件名,還是 2 個(gè)協(xié)議的名稱(chēng)。

AFNetWorking-Serialization.png

二、正文

1. AFURLRequestSerialization(.h/.m)

先看 AFURLRequestSerialization.h 文件,這個(gè)文件里邊共有 2 個(gè)協(xié)議和 3 個(gè)類(lèi),他們之間的關(guān)系如下:

AFURLRequestSerialization-Simple.png

這里,我們以 上一篇 提到的構(gòu)造 request 的 2 個(gè)方法為主線開(kāi)始介紹。

// 方法一

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

先來(lái)看看第一類(lèi)請(qǐng)求方法的實(shí)現(xiàn)中構(gòu)建 MutableURLRequest 用到的方法:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    // 0.檢測(cè) method、URLString 是否為空
    NSParameterAssert(method);
    NSParameterAssert(URLString);

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

    // 1.開(kāi)始創(chuàng)建(系統(tǒng)方法創(chuàng)建)
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.設(shè)置參數(shù)
    // 2.1 請(qǐng)求方法
    mutableRequest.HTTPMethod = method; // 方法默認(rèn)是 “GET”

    // 2.2 為 request 設(shè)置其一些默認(rèn)參數(shù)
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        
        // AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6個(gè)屬性對(duì)應(yīng) getter 方法名 string 組成的數(shù)組
        // self.mutableObservedChangedKeyPaths 中是 值非nil 的屬性名
        // requstSerializer 初始化時(shí),創(chuàng)建了 self.mutableObservedChangedKeyPaths 這個(gè)空 mutableSet,然后,如果設(shè)置了對(duì)應(yīng)的參數(shù),它里邊就會(huì)加上對(duì)應(yīng)的屬性名
        
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    // 3.對(duì)參數(shù)進(jìn)行序列化,并賦值給 request
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

該方法來(lái)自 AFHTTPRequestSerializer,具體功能見(jiàn)上邊的代碼注釋?zhuān)?jiǎn)單看一下部分細(xì)節(jié),2.2 for 循環(huán)的代碼塊,其中 AFHTTPRequestSerializerObservedKeyPaths() 是一個(gè)靜態(tài)函數(shù),實(shí)際返回了當(dāng)前類(lèi) AFHTTPRequestSerializer 中的幾個(gè)重要的屬性名對(duì)應(yīng)的字符串?dāng)?shù)組,也可以認(rèn)為是其 get 方法名對(duì)應(yīng)的字符串?dāng)?shù)組。

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

類(lèi)的初始化方法里邊主要設(shè)置了一些 header,然后就是給上邊方法提到的幾個(gè)屬性添加了 KVO 監(jiān)聽(tīng),當(dāng)用戶給這些屬性賦值 (可以為nil)時(shí),就會(huì)給 self.mutableObservedChangedKeyPaths 這個(gè) mutableSet 增刪元素 (相關(guān)代碼如下):

// 1. init 方法中給屬性添加監(jiān)聽(tīng)的代碼
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }

// 2. 觸發(fā)的監(jiān)聽(tīng)方法
- (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 循環(huán)中的語(yǔ)句,里邊 if 語(yǔ)句的目的是將用戶設(shè)置的屬性值賦給 mutableRequest。

然后,來(lái)到 3. 序列化參數(shù)的時(shí)候,用到了這樣一個(gè)方法 requestBySerializingRequest: withParameters: error:,它屬于 AFURLRequestSerialization 這個(gè)協(xié)議,AFURLRequestSerializer 遵守協(xié)議并實(shí)現(xiàn)了該方法,注釋見(jiàn)下方法注釋。

#pragma mark - AFURLRequestSerialization

// 為 request 設(shè)置參數(shù)
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    // 0.將 request 轉(zhuǎn)成 mutable,以便修改
    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    // 1.設(shè)置 header 字段,self.HTTPRequestHeaders 是在 init 方法中設(shè)置的 useragent 等參數(shù)
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        // 將 self.HTTPRequestHeaders 中的 header 參數(shù)設(shè)置到 request 里邊
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    // 2.參數(shù)序列化
    NSString *query = nil;
    if (parameters) {
        // 2.1 self.queryStringSerialization 是一個(gè) block,如果從外邊 設(shè)置了序列化規(guī)則
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

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

                return nil;
            }
        } else {
         // 2.2 使用AF提供的序列化規(guī)則,將 string、array、dictionary 的 parameters 統(tǒng)一轉(zhuǎn)化成了 key1=value1&key2=value2&key3=value3 格式的字符串
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    // 3.設(shè)置 request 的 URL 或 HTTPBody ??
    
    // 初始化時(shí)已經(jīng)設(shè)置了 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 設(shè)置成 body
        
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        // 如果設(shè)置了 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;
}

至此,第一種創(chuàng)建 mutableRequest 的主要方法就介紹完了,在討論第二種創(chuàng)建 MutableURLRequest 的方法實(shí)現(xiàn)之前,我們先看看 AFNetWorking 中給出的一個(gè)上傳數(shù)據(jù)的示例:

// 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];

關(guān)注一下 constructingBodyWithBlock 中的代碼就行,其中調(diào)用了參數(shù) formData 遵守的一個(gè)協(xié)議方法 appendPartWithFileURL: ...。下面開(kāi)始查看創(chuàng)建 MutableURLRequest 的方法實(shí)現(xiàn):

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

    // 1.創(chuàng)建 mutableRequest
    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

    // 2.構(gòu)建 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.執(zhí)行外部傳入的 block
    if (block) {
        block(formData);
    }

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

大概內(nèi)容見(jiàn)上方代碼注釋?zhuān)旅嫖覀冇懻撘幌录?xì)節(jié)部分。

1.創(chuàng)建 mutableRequest 的方法實(shí)現(xiàn)及注釋如下:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    // 0.檢測(cè) method、URLString 是否為空
    NSParameterAssert(method);
    NSParameterAssert(URLString);

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

    // 1.開(kāi)始創(chuàng)建(系統(tǒng)方法創(chuàng)建)
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.設(shè)置參數(shù)
    // 2.1 請(qǐng)求方法
    mutableRequest.HTTPMethod = method; // 方法默認(rèn)是 “GET”

    // 2.2 為 request 設(shè)置其一些默認(rèn)參數(shù)
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        
        // AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6個(gè)屬性對(duì)應(yīng) getter 方法名 string 組成的數(shù)組
        // self.mutableObservedChangedKeyPaths 中是 值非nil 的屬性名
        // requstSerializer 初始化時(shí),創(chuàng)建了 self.mutableObservedChangedKeyPaths 這個(gè)空 mutableSet,然后,如果設(shè)置了對(duì)應(yīng)的參數(shù),它里邊就會(huì)加上對(duì)應(yīng)的屬性名
        
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    // 3.對(duì)參數(shù)進(jìn)行序列化,與第一種方法調(diào)用的是相同的方法,不過(guò)多介紹
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

2.構(gòu)建 AFStreamingMultipartFormData * 類(lèi)型的 formData,并為其添加數(shù)據(jù)。下面是它的幾個(gè)重要方法:

AFStreamingMultipartFormData 是一個(gè)用于給 request 設(shè)置數(shù)據(jù)的類(lèi)。通過(guò)初始化方法 initWithURLRequest: stringEncoding: ,將 request 賦值給 AFStreamingMultipartFormData 內(nèi)部的 copy 屬性 request,用于后邊的處理及最終返回,。

formData 遵守協(xié)議 AFMultipartFormData,當(dāng)然要實(shí)現(xiàn)其中的一些協(xié)議方法了,幾個(gè)重要的方法 (協(xié)議方法和非協(xié)議方法) 實(shí)現(xiàn)見(jiàn)下方代碼片段。

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

我們發(fā)現(xiàn),其中用到了 AFMultipartBodyStream 和 AFHTTPBodyPart 這 2 個(gè)類(lèi),其中 AFMultipartBodyStream 繼承自 NSInputStream,將會(huì)作為參數(shù)傳給 request。而 AFHTTPBodyPart 用于構(gòu)造分段數(shù)據(jù),并傳遞給 AFMultipartBodyStream。下邊是 2 者的部分方法實(shí)現(xiàn):

// AFMultipartBodyStream : NSInputStream <NSStreamDelegate>

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

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

// 對(duì)所有的 part 組成的數(shù)組進(jìn)行處理:第一個(gè) part 的 hasInitialBoundary = YES,最后一個(gè) part 的 hasFinalBoundary = YES,其他 part 的這兩個(gè)屬性s都是 NO。
// 這個(gè)在計(jì)算分段大小的時(shí)候會(huì)用到
- (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];
    }
}

// 計(jì)算所有分段加起來(lái)的總數(shù)據(jù)
- (unsigned long long)contentLength {
    unsigned long long length = 0;
    for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
        length += [bodyPart contentLength];
    }

    return length;
}
// AFHTTPBodyPart : NSObject

// 計(jì)算每一個(gè)分段的大小
- (unsigned long long)contentLength {
    unsigned long long length = 0;

    // hasInitialBoundary 是前邊 AFMultipartBodyStream 里的 setInitialAndFinalBoundaries 方法里邊設(shè)置過(guò)的哦
    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 也是前邊設(shè)置過(guò)的
    NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
    length += [closingBoundaryData length];

    return length;
}

3.執(zhí)行 block ,其實(shí)也是在給 formData 添加數(shù)據(jù),block 的實(shí)現(xiàn)中必須調(diào)用 AFMultipartFormData 中的協(xié)議方法。

4.返回結(jié)果時(shí),調(diào)用了 formData 自己的方法 requestByFinalizingMultipartFormData(非協(xié)議方法),即為 request 的相關(guān)字段賦值,并將其返回。

最后,我們以這些類(lèi)之間的關(guān)系圖最為本部分的小結(jié):

AFURLRequestSerialization.png

2. AFURLResponseSerialization

如前文所述,AFURLResponseSerialization 即是協(xié)議名稱(chēng),又是文件名。之前在討論 NSURLSessionTaskDelegate 的代理方法 - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error; 時(shí)有這樣一行代碼:

// AFURLSessionManagerTaskDelegate(Class)

// *** 使用 responseSerializer 處理返回結(jié)果
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

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

我們知道,這里的 manager 指的是 AFURLSessionManager,在其 init() 方法中初始化了一個(gè)重要屬性 responseSerializer。那么 AFURLResponseSerialization 這個(gè)文件里邊到底有哪些類(lèi),他們之間的關(guān)系又是怎樣的?

AFURLResponseSerialization.png

從上圖可以發(fā)現(xiàn),這里有一個(gè)重要的基類(lèi) AFHTTPResponseSerializer,他遵守 AFURLResponseSerialization 這個(gè)協(xié)議 (只有一個(gè)協(xié)議方法:- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error;),其它幾個(gè)類(lèi)都是他的子類(lèi),他們都各自實(shí)現(xiàn)了該協(xié)議方法 ,用來(lái)處理接口返回的數(shù)據(jù)。

下面我們從基類(lèi) AFHTTPResponseSerializer 開(kāi)始討論,下面是它的頭文件。

@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>

// 編碼方式,不過(guò)源碼中已經(jīng)指明 It is never used。
@property (nonatomic, assign) NSStringEncoding stringEncoding;
// 可接受的 HTTP 狀態(tài)碼
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
// 可接受的 MIME types
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;


// 創(chuàng)建及初始化方法,創(chuàng)建默認(rèn)配置的 serializer
- (instancetype)init;
+ (instancetype)serializer;

// 校驗(yàn)接口返回的指定 response 和 data,默認(rèn)檢查了 acceptable status code 和 content type
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
                    data:(nullable NSData *)data
                   error:(NSError * _Nullable __autoreleasing *)error;

@end

上邊 3 個(gè)屬性中的第 1 個(gè) stringEncoding 已經(jīng)被標(biāo)記為過(guò)期,即不再對(duì)接收到的數(shù)據(jù)進(jìn)行解碼 (decode)。acceptableStatusCodes 和 acceptableContentTypes 是兩個(gè)集合(set),依次表示客戶端可以接受的響應(yīng)報(bào)文的 HTTP 狀態(tài)碼和 Content-Type,用于后邊對(duì)響應(yīng)報(bào)文 response 和 data 進(jìn)行校驗(yàn)。

再來(lái)看創(chuàng)建及初始化方法的實(shí)現(xiàn),實(shí)際重點(diǎn)在 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 賦初值,前者默認(rèn)為從 200 到 299 之間的整數(shù),及成功的狀態(tài)碼,后者默認(rèn)為 nil,也就是說(shuō)接收任何類(lèi)型 (Content-Type) 的響應(yīng)數(shù)據(jù)。

現(xiàn)在來(lái)看開(kāi)頭提的解析數(shù)據(jù)的方法:- (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;
}

這其實(shí)是 AFURLResponseSerialization 規(guī)定的協(xié)議方法,首先校驗(yàn)了返回的 response 和 data,如果出錯(cuò),就將錯(cuò)誤信息寫(xiě)入 error。最后將 data 返回。用于校驗(yàn)的方法實(shí)現(xiàn)如下:

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

    // 1.response 是 HTTP 響應(yīng)報(bào)文的情況
    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.保存錯(cuò)誤信息-有錯(cuò)誤信息并且response不符合要求的情況
    if (error && !responseIsValid) {
        *error = validationError;
    }

    // 4.返回
    return responseIsValid;
}

代碼看起來(lái)有點(diǎn)長(zhǎng),實(shí)際做的事情比較少,首先判斷響應(yīng)的報(bào)文是否是 HTTP 的響應(yīng)報(bào)文,如果不是,直接返回 responseIsValid (此時(shí)為 YES),如果是 HTTP 報(bào)文時(shí)才會(huì)進(jìn)行接下來(lái)的判斷。接著分別判斷 StatusCode 和 ContentType 是否符合要求,如果不符合要求,則構(gòu)建相應(yīng)的 validationError 信息,并將 responseIsValid 置為 NO。

當(dāng) error 存在,并且 responseIsValid 為 NO 時(shí),將 validationError 賦值給 error。最后返回校驗(yàn)結(jié)果。

至于 AFHTTPResponseSerializer 的子類(lèi),我們就只看一下 AFJSONResponseSerializer,其他子類(lèi)與之類(lèi)似,就不多做介紹了。

AFJSONResponseSerializer 是專(zhuān)門(mén)用于解析 JSON 格式的響應(yīng)數(shù)據(jù)的。他重寫(xiě)了父類(lèi)的初始化方法,給 acceptableContentTypes 添加了 application/json、text/json、text/javascript 這 3 中 json 相關(guān)的格式。

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

同時(shí)通過(guò)兩個(gè)工廠方法為屬性 readingOptions 賦初值:

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

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

    return serializer;
}

最后,重寫(xiě)了解析數(shù)據(jù)的方法 (如下),先是和父類(lèi)一樣校驗(yàn)了 ContentType 和 StatusCode,然后反序列化拿到的響應(yīng)數(shù)據(jù),過(guò)濾掉反序列化后的數(shù)據(jù)中的 value 為 Null 的數(shù)據(jù),最后將處理完的數(shù)據(jù)返回。

#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    // 1.調(diào)用父類(lèi)方法,校驗(yàn) 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.反序列化數(shù)據(jù)
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
    if (!responseObject)
    {
        if (error) {
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    
    // 3.移除 value 為 Null 的 鍵值對(duì)
    if (self.removesKeysWithNullValues) {
        return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    }

    return responseObject;
}

以上就是構(gòu)建及序列化請(qǐng)求參數(shù)的 AFHTTPRequestSerializer 和 處理響應(yīng)數(shù)據(jù)的 AFHTTPResponseSerializer 的主要實(shí)現(xiàn)代碼,當(dāng)然還有很多細(xì)節(jié),在此就不再多做介紹了。

參考

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容