以上為個(gè)人愚見, 如有不妥,望大家斧正!!!
本文的GitHub源碼下載地址:
https://github.com/DXSmile/Bug---AFNetworking.git
如需轉(zhuǎn)載,請注明轉(zhuǎn)載自DXSmile的
GitHub項(xiàng)目https://github.com/DXSmile/Bug---AFNetworking.git
闡述:
在獲取網(wǎng)絡(luò)數(shù)據(jù)的時(shí)候, 我們一般會(huì)使用到一個(gè)非常著名的框架: AFNetworking框架, 可以說,這是作為iOS攻城獅必備的框架之一;
這個(gè)框架是非常強(qiáng)大的一個(gè)框架, 對于發(fā)送異步請求來說,簡直沒有比這個(gè)更好用的了, 不過,在使用的過程中,我們可能會(huì)遇到這樣一個(gè)bug: 如下
連接出錯(cuò) Error Domain=com.alamofire.error.serialization.response Code=-1016
"Request failed: unacceptable content-type: text/html" UserInfo=
{com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x7f93fad1c4b0>
{ URL: http://c.m.163.com/nc/article/headline/T1348647853363/0-140.html }
{ status code: 200, headers { .....}
......
22222c22 626f6172 64696422 3a226e65 77735f73 68656875 69375f62 6273222c 22707469 6d65223a 22323031 362d3033 2d303320 31313a30 323a3435 227d5d7d>,
NSLocalizedDescription=Request failed: unacceptable content-type: text/html}
說明:
由于數(shù)據(jù)很多,所以返回的請求體,和響應(yīng)體部分我用省略號(......)代替了, 但是,通過上面的返回的信息,我們不難看出,狀態(tài)碼是200, 而且也有一堆數(shù)據(jù), 但是在tableviewCell中就是沒有顯示, 在最后的時(shí)候還出現(xiàn)"NSLocalizedDescription=Request failed: unacceptable content-type: text/html} " 這樣一句話;
分析:那么這個(gè)錯(cuò)誤是什么原因造成的呢?
通過這句話:unacceptable content-type: text/html,我們可以看出報(bào)錯(cuò)原因:是不接收的內(nèi)容類型,也就是說AFNetworking框架不支持解析text/html這種格式. 那么怎樣解決呢?
首先我們需要明白: AFNetworking為什么能夠解析服務(wù)器返回的東西呢?
因?yàn)閙anager有一個(gè)responseSerializer屬性.它只設(shè)置了一些固定的解析格式.其中不包含text/html這種數(shù)據(jù)的格式.因?yàn)榻馕鰣?bào)錯(cuò)了.
我們來看一下AFNetworking解析格式的底層:
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
通過底層,我們也可以看見,確實(shí)是沒有text/html這種數(shù)據(jù)的格式的,
那如何解決這個(gè)問題呢?
錯(cuò)誤的解決方法
下面我嘗試了三種方法:
解決方法1: 直接給acceptableContentTypes屬性添加類型
解決之前:
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
著手解決:
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManagermanager];
// 添加 text/html 類型到可接收內(nèi)容類型中
mgr.responseSerializer.acceptableContentTypes= [NSSetsetWithObjects:@"text/html", nil];
運(yùn)行結(jié)果:
通過執(zhí)行結(jié)果可以很明顯的看得出,我們已經(jīng)非常成功的獲取到了數(shù)據(jù);
對方法1的思考:
**首先,我們可以明顯的看出,方法1確實(shí)是可以解決問題的,但是這樣解決真的好嗎? 不一定!
為什么呢? 很簡單, 如果我們只是發(fā)送一條網(wǎng)絡(luò)請求,無疑方法1是最恰當(dāng)?shù)慕鉀Q方案了, 但是實(shí)際開發(fā)中,我們不可能只發(fā)一次請求, 那么就需要我們每次發(fā)請求的時(shí)候都來寫一次這些代碼; 當(dāng)然,如果您愿意寫,那我也沒辦法多說什么了;
很顯然,這個(gè)方法是存在不足的! **
于是我們有了第二種方法:
解決辦法2: 直接到框架的源代碼中添加類型
解決之前:
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
解決之后:
self.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];
分析方法2:
不得不說,這也是一種辦法, 而且釜底抽薪,效果方面呢,當(dāng)然也是顯而易見了, 但是, 注意了,這個(gè)方法2 又真的恰當(dāng)嗎? 真的好嗎?
我們來假設(shè)一種情況, 而且實(shí)際開發(fā)中必然會(huì)發(fā)生的情況: 這個(gè)框架更新了!!!
對,就是更新了!!! 更新了顯然又會(huì)回到之前的狀態(tài)
傻眼了吧? **
實(shí)際開發(fā)中,我們都會(huì)用cocoaPods來管理我們的第三方框架, 當(dāng)某個(gè)框架更新之后, cocoaPods會(huì)下載最新的框架源碼鑲嵌到我們的項(xiàng)目中, 我們并不能保證AFNetworking這個(gè)框架一定會(huì)把我們需要的類型添加上去, 所以每一次更新,我們都需要針對源碼再做一次修改**
很顯然,這也是費(fèi)力不討好的;
那么有沒有一勞永逸的方法呢? 別急,馬上就來!!!
解決辦法3: 自定義一個(gè)manager ,拓展一個(gè)類型
>>這里需要考慮到兩種情況: 如下
>1. 如果你的APP只需要適配iOS7.0之前的版本,為了能夠適配舊系統(tǒng),需要使用 AFHTTPRequestOperationManager
>2. 如果你的APP只需要適配iOS7.0之后的版本,那么你需要 自定義的類是繼承AFHTTPSessionManager的
這里我只簡單的介紹iOS7.0之后的版本,
1> 自定義manager,繼承自AFHTTPSessionManager
@interface DXHTTPManager : AFHTTPSessionManager
2> 在.m文件中,重寫父類的manager方法 目的 : 添加類型
+ (instancetype)manager {
DXHTTPManager *mgr = [super manager];
// 創(chuàng)建NSMutableSet對象
NSMutableSet *newSet = [NSMutableSet set];
// 添加我們需要的類型
newSet.set = mgr.responseSerializer.acceptableContentTypes;
[newSet addObject:@"text/html"];
// 重寫給 acceptableContentTypes賦值
mgr.responseSerializer.acceptableContentTypes = newSet;
return mgr;
}
3> 在發(fā)送請求的時(shí)候,使用我們自定義的類來發(fā)送請求
[[DXHTTPManager manager] GET:@"http://...."
parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary * responseObject)
{
NSLog(@"請求成功 -- %@",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"請求失敗 -- %@",error);
}];
執(zhí)行結(jié)果:
結(jié)論:
**1. 使用方法3來解決這個(gè)bug,雖然看似比第一種,第二種要繁瑣一些,實(shí)則可拓展性,和維護(hù)方面,要好得多,以后我們開發(fā)項(xiàng)目的時(shí)候, 只需要將我們自定義的這個(gè)類拖進(jìn)去就可以了, 假如有需要新的類型的時(shí)候, 也只是簡單的多配置一下類型即可, **
2.此外方法3還有一個(gè)好處,我們假設(shè)一個(gè)情景: 如果有一天我們使用的這個(gè)AFNetworking框架不再更新了,甚至作廢了呢? 這種可能性還是有的,如果我們用方法1和方法2的話, 那個(gè)時(shí)候你會(huì)在項(xiàng)目中看見一堆一堆的bug,
而如果我們使用的方法3的話,那么只需要簡單的將繼承的類更換成我們最新使用的框架里面的類,然后做一些簡單的配置即可,這樣維護(hù)起來的成本自然就輕松了,
3.而這也正是我們代碼重構(gòu), 和優(yōu)化項(xiàng)目架構(gòu)的思路之一!!!