[AFN]使用AFNetworking遇到的問題總結(jié)

1. 使用 BaseURL 發(fā)送請求, 丟失地址路徑

在維護(hù)網(wǎng)絡(luò)請求的 API 時(shí), 一般都是使用 “baseURL + 相對地址” 的方式, 即:

#define baseURL @"https://www.baidu.com/"
#define login @"/login"

這樣在使用的時(shí)候, 沒什么問題, 但是, 如果你是下面這樣定義的, 可能就有問題了:

#define baseURL @"https://www.baidu.com/app/"
#define login @"/login"

會(huì)一直打印 404 的錯(cuò)誤信息, 而地址變成了 https://www.baidu.com/login

最后, 查看源碼, 發(fā)現(xiàn)問題出在, AFN 內(nèi)使用的這么一個(gè)方法使用上

+ (nullable instancetype)URLWithString:(NSString *)URLString relativeToURL:(nullable NSURL *)baseURL;

這是系統(tǒng)的一個(gè)方法, 具體原因可以查看這篇文章: ios-NSURL URLWithString:relativeToURL的坑

定義 API 的時(shí)候, 要么 baseURL 只用 host 地址, 要么就按下面的格式來定義:

  1. baseURL 使用 / 結(jié)尾
  2. 相對路徑開頭不能加 /

2. The data couldn’t be read because it isn’t in the correct format

這個(gè)問題是使用AFN發(fā)送POST請求時(shí)遇到的,奇怪的是使用GET請求能夠正常拿到數(shù)據(jù),而使用POST就不行;
控制臺輸出如下信息:

http://192.168.69.121:8080/artboss-webapp/ios/checkcode  
 params:{  
    "user_phonenumber" = 185****0925;  
}  
 errorInfos:The data couldn’t be read because it isn’t in the correct format.  

大意就是數(shù)據(jù)沒有正確的被格式化,無法讀取!后臺也接收不到我傳遞的參數(shù);
使用Charles抓包會(huì)發(fā)現(xiàn)是這樣的信息:

HTTP Status 400 - Request String parameter 'user_phonenumber' is not present  

400 Bad Request!

我一看是非法的網(wǎng)絡(luò)請求,以為是我的問題,就一直在我這邊找原因,
最后才發(fā)現(xiàn),這是因?yàn)槲野l(fā)送請求時(shí)發(fā)送的字符串,和后臺需要的字符串格式不一致,發(fā)送請求時(shí),后臺需要的是text文本格式,而我發(fā)送的是json格式,導(dǎo)致后臺無法識別,接收不到數(shù)據(jù)!
后來發(fā)現(xiàn)我所用的封裝類里的請求頭,有如下設(shè)置:

[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];  
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];  

即,默認(rèn)是使用json傳輸數(shù)據(jù)的!
把第二個(gè)注釋掉,即使用默認(rèn)的Content-Type,
并在發(fā)送請求時(shí)設(shè)置發(fā)送的字段為文本格式,即:

manager.requestSerializer = [AFHTTPRequestSerializer serializer];  

本以為會(huì)解決問題,但事實(shí)還是請求不到數(shù)據(jù)!!
最后不得已使用原生的AFNetworking進(jìn)行測試,post正常拿到了數(shù)據(jù),后來對比兩者的區(qū)別,除了上面的改動(dòng)以外,請求返回的數(shù)據(jù)也要改為文本,即:

manager.responseSerializer = [AFHTTPResponseSerializer serializer]; 

這樣才正常拿到了數(shù)據(jù)!!
總之,此次網(wǎng)絡(luò)請求的異常,是由于請求和響應(yīng)的文本格式?jīng)]有統(tǒng)一,導(dǎo)致參數(shù)無法正常傳遞,數(shù)據(jù)無法正常讀取!

3. Invalid parameter not satisfying: body

在使用AFNetworking進(jìn)行傳圖操作的時(shí)候,出現(xiàn)了這個(gè)crash信息:
模擬器運(yùn)行后控制臺輸出:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: body'  
*** First throw call stack:  
(  
    0   CoreFoundation                      0x0000000107ad4e65 __exceptionPreprocess + 165  
    1   libobjc.A.dylib                     0x0000000106c1edeb objc_exception_throw + 48  
    2   CoreFoundation                      0x0000000107ad4cca +[NSException raise:format:arguments:] + 106  
    3   Foundation                          0x000000010431d4de -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198  
)  
libc++abi.dylib: terminating with uncaught exception of type NSException  

真機(jī)的話會(huì)輸出如下信息:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: body'  
*** First throw call stack:  
(0x225e62eb 0x21db2dff 0x225e61c1 0x22dbcd3b 0x1b8d3f 0x1b8981 0x1909f1 0x1b4a6b 0x1a1be9 0x1906c1 0x176535 0x177171 0x193371 0x18f5cb 0x19fbb9 0x8dfcbf 0x8dfcab 0x8e4771 0x225a8fc5 0x225a74bf 0x224f9bb9 0x224f99ad 0x23773af9 0x267e5fb5 0x118b11 0x221ac873)  
libc++abi.dylib: terminating with uncaught exception of type NSException  
(lldb)   

而且crash的地方也不一樣:
模擬器crash到自動(dòng)釋放池

模擬器crash

真機(jī)的話carsh到AFN的底層:

真機(jī)crash

其實(shí),最主要的信息就是:Invalid parameter not satisfying: body (無效的參數(shù):body)
問題就出在body這個(gè)參數(shù)上,模擬器上看不出什么頭緒,請求參數(shù)中也沒有body這個(gè)參數(shù);但是在真機(jī)上的crash信息可以看出一些頭緒:他是crash到了這個(gè)方法里

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

可以看到body的類型是NSData,而設(shè)置的請求參數(shù)中,只有要發(fā)送的照片數(shù)據(jù)是NSData類型,是不是照片的問題呢?進(jìn)到AFN的底層可以發(fā)現(xiàn),AFN上傳圖片主要是用到了這個(gè)方法:

- (void)appendPartWithFileData:(NSData *)data  
                          name:(NSString *)name  
                      fileName:(NSString *)fileName  
                      mimeType:(NSString *)mimeType  
{  
    NSParameterAssert(name);  
    NSParameterAssert(fileName);  
    NSParameterAssert(mimeType);  
  
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];  
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];  
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];  
  
    [self appendPartWithHeaders:mutableHeaders body:data];  
}  

在這個(gè)方法里調(diào)用了

[self appendPartWithHeaders:mutableHeaders body:data];

正是,程序crash的地方;
其實(shí),打斷點(diǎn)調(diào)試后也能發(fā)現(xiàn)傳入的照片數(shù)據(jù)為nil,問題的根源找到了,問題也就解決了!!!

4. terminating with uncaught exception of type NSException

雖然上面那個(gè)問題中也有這句輸出,但是這次除了這句沒有其他信息,上面的還有其他比較多的信息以供排查問題,這次是這樣的:


什么鬼都看不出來...

檢查了各種參數(shù),都沒有問題,最后不得已,斷點(diǎn)一步步執(zhí)行,最后發(fā)現(xiàn)是請求時(shí)的數(shù)據(jù)格式問題;
AFN的默認(rèn)請求的數(shù)據(jù)格式為JSON,這也是大多數(shù)后臺使用的數(shù)據(jù)格式,同樣返回格式也是JSON;
但是,但是,但是...重要的事情說三遍,總有不支持JSON的后臺接口.......
最后,我設(shè)置了一下請求頭的請求數(shù)據(jù)格式,即:

manager.requestSerializer = [AFHTTPRequestSerializer serializer];  

最后就可以正常發(fā)送請求了;
這也是因?yàn)榘l(fā)送請求時(shí)的數(shù)據(jù)格式問題,所以,一定要和后臺溝通好, 一定要和后臺溝通好, 一定要和后臺溝通好....

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

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

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