當(dāng)訪問一個(gè)網(wǎng)頁的時(shí)候,NSURLRequest會(huì)默認(rèn)幫你主動(dòng)記錄當(dāng)前站點(diǎn)的設(shè)置的Cookie,然后訪問之后就會(huì)自動(dòng)寫入磁盤,iOS是把cookie信息放在的NSHTTPCookieStorage容器中管理,當(dāng)下一次請(qǐng)求的時(shí)候,NSURLRequest會(huì)從內(nèi)存中拿出上次保存下來的Cookie繼續(xù)去請(qǐng)求。
Cookie
Cookie有服務(wù)器端生成,響應(yīng)后發(fā)送給客戶端,每一個(gè)Cookie都是一個(gè)NSHTTPCookie類的實(shí)例。
NSHTTPCookieStorage
NSHTTPCookieStorage實(shí)現(xiàn)了一個(gè)管理cookie的單例對(duì)象(只有一個(gè)實(shí)例),每個(gè)Cookie都是NSHTTPCookie類的實(shí)例。同一個(gè)域下的Cookie在應(yīng)用內(nèi)是共享的。
HTTPHeader
HTTPHeader中包含HTTP請(qǐng)求和響應(yīng)的參數(shù),header中定義了傳輸數(shù)據(jù)的各種特性。header以屬性名開始,屬性值之間用;間隔 。Cookie屬于HTTPHeader的一部分。
iOS中Cookie的讀取和寫入
Cookie一般是通過http的Response中傳過來,作為Response的HTTP Header中,系統(tǒng)默認(rèn)會(huì)將cookie寫入磁盤保存。那么如何獲取Cookie,并且寫入自定義Cookie呢?
需要先手動(dòng)獲取當(dāng)前NSHTTPCookieStorage中的所有cookie,然后將cookie放到NSURLRequest請(qǐng)求頭中
1.獲取已有的Cookie,然后添加新的Cookie。
- (NSArray *)fetchNewCookies {
NSMutableArray *cookies = [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:@"https://backend.gmei.com"]] mutableCopy];
NSHTTPCookie *accessCookie = [self fetchAccessTokenCookie];
//添加新Cookie最好多加一步去重
[cookies addObject:accessCookie];
return [cookies copy];
}
- (NSHTTPCookie *)fetchAccessTokenCookie {
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
[properties setObject:@"_gm_token" forKey:NSHTTPCookieName];
[properties setObject:accessToken forKey:NSHTTPCookieValue];
[properties setObject:@"backend.gmei.com" forKey:NSHTTPCookieDomain];
[properties setObject:@"/" forKey:NSHTTPCookiePath];
NSHTTPCookie *accessCookie = [[NSHTTPCookie alloc] initWithProperties:properties];
return accessCookie;
}
iOS中NSHTTPCookie有一系列的鍵值對(duì)作為Cookie的屬性
HTTPCookie屬性中的key有:
NSString *NSHTTPCookieComment;
NSString *NSHTTPCookieCommentURL;
NSString *NSHTTPCookieDiscard;
NSString *NSHTTPCookieDomain;
NSString *NSHTTPCookieExpires;
NSString *NSHTTPCookieMaximumAge;
NSString *NSHTTPCookieName;
NSString *NSHTTPCookieOriginURL;
NSString *NSHTTPCookiePath;
NSString *NSHTTPCookiePort;
NSString *NSHTTPCookieSecure;
NSString *NSHTTPCookieValue;
NSString *NSHTTPCookieVersion;
下面簡(jiǎn)單解釋下每個(gè)key的含義。
NSHTTPCookieComment
包含cookie的注釋的NSString對(duì)象,注釋項(xiàng),用戶說明該 Cookie 有何用途,對(duì)Vesion1以及以后的版本有效,是可選屬性NSHTTPCookieCommentURL
包含cookie的CommentURL的NSURL/NSString對(duì)象,對(duì)Vesion1以及以后的版本有效,是可選屬性。NSHTTPCookieDiscard
一個(gè)NSString對(duì)象,用于標(biāo)識(shí)在會(huì)話結(jié)束時(shí)是否應(yīng)該丟棄改Cookie,是可選屬性。String的值必須是True或者false。默認(rèn)值是false,除非該cookie是version1或更高版本,否則不指定NSHTTPCookieMaximumAge的值,在這種情況下,它被假定為TRUENSHTTPCookieDomain
包含cookie的域的NSString對(duì)象,如果缺少此cookie屬性,則會(huì)根據(jù)NSHTTPCookieOriginURL的值推斷該域。如果未指定NSHTTPCookieOriginURL的值,則必須為NSHTTPCookieDomain指定值。NSHTTPCookieExpires
指定了Cookie過期日期的NSDate對(duì)象或NSString對(duì)象,此cookie屬性僅用于版本0的Cookie,是可選屬性NSHTTPCookieMaximumAge
個(gè)包含一個(gè)整數(shù)值的NSString對(duì)象,表示cookie最大失效時(shí)間。僅適用于版本1 Cookie及更高版本。默認(rèn)值為“0”。此cookie屬性是可選的。NSHTTPCookieName
包含cookie名稱的NSString對(duì)象。此cookie屬性是必選的。NSHTTPCookieValue
包含cookie的值的NSString對(duì)象。此cookie屬性是必選的。NSHTTPCookieOriginURL
包含設(shè)置此cookie的URL的NSURL或NSString對(duì)象。如果您不提供NSHTTPCookieOriginURL的值,則必須為NSHTTPCookieDomain提供一個(gè)值。NSHTTPCookiePath
該Cookie是在當(dāng)前的哪個(gè)路徑下生成的,/表示在當(dāng)前目錄,此cookie屬性是必選的。NSHTTPCookiePort
NSString對(duì)象,包含逗號(hào)分隔的整數(shù)值,指定Cookie的端口。僅適用于version1或更高版本。默認(rèn)值為空字符串,是可選的。NSHTTPCookieSecure
一個(gè)NSString對(duì)象,如果設(shè)置了這個(gè)屬性,那么只會(huì)在 SSH 連接時(shí)才會(huì)回傳該 Cookie。默認(rèn)是fasleNSHTTPCookieVersion
Cookie有兩個(gè)版本:Version 0 和 Version 1。通過它們有兩種設(shè)置響應(yīng)頭的標(biāo)識(shí),分別是 “Set-Cookie”和“Set-Cookie2”。該屬性的值必須是“0”或“1”。默認(rèn)值為“0”,是可選的。
2.Cookie的本地同步和動(dòng)態(tài)綁定
1的操作首先是構(gòu)造需要寫入的自定義Cookie,構(gòu)造成功后,因?yàn)镽esponse中的Cookie不僅僅只有一個(gè),所以我們通過url獲取到當(dāng)前域下的Cookie得到的是一個(gè)數(shù)組Cookies,然后將自定義的Cookie添加到原有的Cookies數(shù)組中得到新的Cookies。
NSMutableArray *cookies = [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:@"https://backend.gmei.com"]] mutableCopy];
Okay,現(xiàn)在新Cookies我們已經(jīng)構(gòu)造好了。需要做的是,再新的http請(qǐng)求到來的時(shí)候,新的http請(qǐng)求的頭部中的Cookies是我們構(gòu)造好的Cookie。
- 場(chǎng)景1 自定義的Cookie的value是常量
如果我們僅僅寫入的一個(gè)Cookie值是一個(gè)常量(當(dāng)然很少見),我們可以直接將新的Cookies交由NSHTTPCookieStorage管理,執(zhí)行完這段代碼后,之后的請(qǐng)求Header中的Cookies就是新的Cookies。如果fetchAccessTokenCookie方法中設(shè)置一個(gè)例如24小時(shí)的過期時(shí)間的key,那么24小時(shí)內(nèi),新的Cookies在客戶端都是有效的。
統(tǒng)提供setCookie方法,需要注意的是如果兩個(gè)cookie的name名字相同,后面的會(huì)覆蓋已經(jīng)存在的cookie。
/*!
@method setCookie:
@abstract Set a cookie
@discussion The cookie will override an existing cookie with the
same name, domain and path, if any.
*/
[- (void)setCookie:(NSHTTPCookie *)cookie;]
執(zhí)行就okay了
NSHTTPCookie *accessCookie = [self fetchAccessTokenCookie];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:accessCookie];
- 場(chǎng)景2 自定義的Cookie的value是變量且要求立即生效,不能遺漏任何一個(gè)http請(qǐng)求。
在這種要求生效的及時(shí)性和完整性的時(shí)候,上面的方案1就無法滿足。這個(gè)時(shí)候我們需要將新的Cookies和接下來的請(qǐng)求url進(jìn)行綁定。Cookie是HTTPHeader的一部分,所以現(xiàn)將得到的新Cookies,生成一個(gè)HTTPHeader,系統(tǒng)提供了將 cookie轉(zhuǎn)成請(qǐng)求頭的方法。
requestHeaderFieldsWithCookies:
UIWebView
NSHTTPCookie *accessCookie = [self fetchAccessTokenCookie];
NSDictionary *requestHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:accessCookie];
[request setValue:[requestHeaders objectForKey:@"Cookie"] forHTTPHeaderField:@"Cookie"];
[_webView loadRequest:request];
AFNetworking
GET/PSOT/DELETE/PUT/PATCH
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer.HTTPShouldHandleCookies = YES;
[manager.requestSerializer setValue:newCookies forHTTPHeaderField:@"Cookie"];
對(duì)于上傳音視頻的時(shí)候,AFNetworking用的是AFURLSessionManager而不是AFHTTPRequestOperationManager。
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:URLString parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
//沒有指定name的時(shí)候,name不能為空,默認(rèn)填充 @"file" 即可
[formData appendPartWithFileData:data name:name fileName:fileName mimeType:mineType];
} error:nil];
request.timeoutInterval = 30;
NSHTTPCookie *accessCookie = [self fetchAccessTokenCookie];
NSDictionary *requestHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:accessCookie];
[request setValue:[httpHeader objectForKey:@"Cookie"] forHTTPHeaderField:@"Cookie"];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:NULL completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (error) {
[self failureWithTask:nil error:error failed:failed];
} else {
[self successWithTask:nil responseObject:responseObject success:success];
}
}];
[uploadTask resume];
Alamofire
var cookies = HTTPCookieStorage.shared.cookies(for: URL(string: @"http://backend.gmei.com")
if let cookie = fetchAccessTokenCookie() {
HTTPCookieStorage.shared.setCookie(cookie)
HTTPCookieStorage.shared.cookieAcceptPolicy = .always
cookies?.append(cookie)
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
let dataRequest = Alamofire.request(urlString, method:
method, parameters: parameters, encoding:
URLEncoding.default, headers: headers)
dataRequest.validate(statusCode: 200..<400)
.responseData { (response) in
if let data = response.result.value,
response.result.isSuccess {
} else {
}
}
}