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

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

這里,我們以 上一篇 提到的構(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é):

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)系又是怎樣的?

從上圖可以發(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