問題:
最近在做一個登錄相關(guān)的迭代開發(fā)時唠亚,后臺對cookie的支持升級蚪战,變的更加“標準”牵现,即:由 原來附在請求頭的set-cookie是只對應一個value 變成 對應多個value。要求前段和移動端進行適配接收邀桑,保存新的set-cookie瞎疼,并且在其他接口上傳該結(jié)構(gòu)的cookie。
分析:
問題分為兩大部分壁畸,一是接收并解析保存贼急,二是訪問其他接口附帶上傳茅茂。因為接收解析相對簡單,蘋果有專用的NSHTTPCookie類支持太抓,網(wǎng)上也有很多資料空闲,本文就不多贅述,僅僅帶一下走敌。關(guān)于第二點进副,筆者百度了很久都沒有合適的解決方案,最后還是在和后臺的聯(lián)調(diào)中摸索而出悔常,故寫本文以作記錄影斑。
一、接收机打,解析矫户,保存
這個比較簡單,無論是AFNetworking還是NSHTTPCookie類都簡潔易用残邀,直接上代碼:
//從AFNetworking的post請求中獲得請求頭皆辽,該頭的格式為NSDictionary
//其中operation是指AFHTTPRequestOperation *operation
NSDictionary *allHeaderFieldDict = [operation.response allHeaderFields];
//用NSHTTPCookie直接將請求頭中的setCookie轉(zhuǎn)成鍵值對形式的數(shù)組,后面用到芥挣。另外說下NSHTTPCookie會自動過濾非setCookie的鍵值對驱闷。
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:allHeaderFieldDict forURL:[NSURL URLWithString:url]];
//用字符串的形式拿到set-Cookie對應的value
NSString *setCookie = [allHeaderFieldDict objectForKey:@"Set-Cookie"];
這里說下,后臺給我的set-Cookie是多個的空免,多個是個什么概念呢?后臺給我了一張圖空另,便于理解是不是感到很疑惑,如果是key-value的形式蹋砚,眾所周知一個Map(或者說是字典)里扼菠,key是唯一的,不可能是一個“set-Cookie”作為key對應多個value坝咐。驗證這一點我們可以打印下上面代碼中的setCookie字符串循榆,看取到了什么?
id=*****; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/,
mobile=***********; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/,
timestart=2017/11/23 13:48:20; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/,
timespan=15; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/,
identifier=85EF24DAF969423EA129C6A15863936B; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/,
token=ae9721de81f3b6c336ccd94abae9c698; domain=.*******.com; expires=Fri, 08-Dec-2017 05:48:20 GMT; path=/
這里要著重說明三點秧饮,這三點在上傳時很重要。
- OC中key-value是標準的一對一泽篮,所以后臺發(fā)的圖1那種形式被轉(zhuǎn)成了圖2的樣子盗尸。其vaule是包含上圖所有value的字符串,這個字符串的格式很重要_淙琛U窭汀!
- 我進行了換行操作油狂,分成了6行,注意從第二行開始位置錯了一個空格。這不是我要逼死強迫癥的操作专筷,而是原字符串在第一行結(jié)尾和第二行開頭中間本來就有一個空格弱贼。我實在想不出怎么體現(xiàn)這個空格就出此下策。要是還不信你就仔細看圖2里面是不是真的有空格磷蛹。為什么說這么多廢話強調(diào)一個空格吮旅,因為格式很重要!N犊取庇勃!
- 還是格式問題,每個;和,的后面空格槽驶,仔細觀察责嚷。格式很重要!5囝怼罕拂!
太長了爆班,截不下,不過不影響理解辱姨,從上圖我們可以方便的看出圖1的格式柿菩。而且這中方法比較好的點在于,不在乎后面的一個key對應了多少個value雨涛,有幾個value數(shù)組就有幾個元素碗旅。像我公司原來的后臺就只有1個value還能用字符串去取,現(xiàn)在改成多個了镜悉,顯然這個方法更利于理解祟辟。
接收到,解析完侣肄,儲存的話旧困,我相信你肯定會存字符串,也肯定會存數(shù)組吧稼锅。
二吼具、訪問時上傳
一般的cookie上傳如下:
//manager即[AFHTTPRequestOperationManager manager];返回的單例
//cookStr即cookie
[manager.requestSerializer setValue:cookStr forHTTPHeaderField:@"Cookie"];
事實上AFNetworking只有這一個方法將一個鍵值對加入請求頭,同樣value自然也必須是字符串矩距。
對于我程序中遇到的多個cookie
- 我考慮過將幾個鍵值對變成字典然后用上面的方法放入請求頭拗盒,但顯然字典不符合value必須是String的設定。
- 然后我有考慮將字典轉(zhuǎn)成JSONString放入value,但后臺拿到的是帶有轉(zhuǎn)義字符的字符串锥债,無法解析陡蝇。
- 如果將cookie分別以自己的key(比如id,mobile,timestart等)分別加入HTTPHeaderField又不符合后臺的標準化需求痊臭。
- 后臺反復強調(diào)將cookie以發(fā)送給我的方式返回,經(jīng)過再三調(diào)試和思考登夫,發(fā)現(xiàn)拿到的cookie其本質(zhì)是一個字符串广匙,但這個字符是有格式的!(所以說格式很重要D詹摺Q恢隆!)
最后經(jīng)過調(diào)試涣楷,返回了如下格式的字符串,*為手動打碼
NSString * cookStr = @"id=*****; mobile=***********; timestart=2017/11/23 13:48:20; timespan=15; identifier=85EF24DAF969423EA129C6A15863936B; token=ae9721de81f3b6c336ccd94abae9c698"
注意分唾,id前面與token最后面沒有空格,但每個;后面有一個空格狮斗。token字符串最后沒有;
[manager.requestSerializer setValue:cookStr forHTTPHeaderField:@"Cookie"];
將以上字符放入請求頭后绽乔,經(jīng)過請求后臺反饋可以解析返回的cookie。
長求總:
如果你的后臺需要在請求頭里返回一個包含若干個cookie的set-Cookie情龄,你可以按以下格式嘗試返回迄汛。務必切記字符串中哪里有空格哪里沒空格。
NSString *cookStr = @"key1=value1; key2=value2; key3=value3; key4=value4"
[manager.requestSerializer setValue:cookStr forHTTPHeaderField:@"Cookie"];
最后夾帶點私貨 /滑稽
- 這個是將圖2字符串處理成最后上傳的字符串的方法,按需自取
/**
* 其中的需要傳入?yún)?shù)在本文第一段中有詳細代碼
*/
-(NSString *)quickLoginMakeCookieWithCookiesArray:(NSArray *)cookies withCookieString:(NSString *)setCookie {
NSMutableArray *titleArray = [[NSMutableArray alloc] init];
for (NSHTTPCookie *cookie in cookies) {
[titleArray addObject:[NSString stringWithFormat:@"%@=",cookie.name]];
}
NSMutableString *lastString = [[NSMutableString alloc] init];
for (NSString *str in titleArray) {
NSRange tokenRange = [setCookie rangeOfString:str];
NSInteger beginLoc = tokenRange.location+tokenRange.length;
NSInteger count = 0;
for (;;) {
count++;
NSString *emunStr = [setCookie substringWithRange:NSMakeRange(beginLoc++, 1)];
if ([emunStr isEqualToString:@";"]) {
break;
}
}
NSString *valueString = [setCookie substringWithRange:NSMakeRange(tokenRange.location+tokenRange.length, --count)];
NSString *resultString = [NSString stringWithFormat:@"%@%@",str,valueString];
if ([str isEqualToString:@"id="]) {
[lastString appendFormat:@"%@",resultString];
}
else {
[lastString appendFormat:@"; %@",resultString];
}
}
return lastString;
}
- 解決聯(lián)調(diào)中格式或者參數(shù)不對時候的一個小Tip骤视,一般來說后臺自己在開發(fā)完代碼后也會用瀏覽器插件/模擬器發(fā)起一個網(wǎng)絡請求來調(diào)試代碼鞍爱。當然這個東西也是可以打印參數(shù)的。如果搞不定你的發(fā)送參數(shù)的話可以讓他把參數(shù)打印出來給你专酗,你對比你發(fā)送的參數(shù)睹逃,就能找到問題的所在了。
- 最后祷肯,若有錯誤沉填,懇請斧正。