NSURLSession

一、NSURLSession概覽

1 NSURLSession的類型

  • 默認(rèn)會(huì)話(Default Sessions)使用了持久的磁盤(pán)緩存,并且將用戶的證書(shū)存入鑰匙串
  • 臨時(shí)會(huì)話(Ephemeral Sessions)沒(méi)有向磁盤(pán)中存入任何數(shù)據(jù),所有相關(guān)內(nèi)容都存在RAM中,當(dāng)會(huì)話無(wú)效時(shí),證書(shū)及緩存的數(shù)據(jù)都會(huì)被清除掉。
  • 后臺(tái)會(huì)話(Background Sessions)除了使用了一個(gè)單獨(dú)的線程來(lái)處理會(huì)話之外,與默認(rèn)會(huì)話類似。但使用后臺(tái)會(huì)話有一些限制,比如會(huì)話必須提供事件交付的代理方法、只有HTTP和HTTPS協(xié)議支持后臺(tái)會(huì)話、總是伴隨著重定向。僅僅在上傳文件時(shí)才支持后臺(tái)會(huì)話,當(dāng)你上傳二進(jìn)制對(duì)象或者數(shù)據(jù)流時(shí)是不支持后臺(tái)會(huì)話的。當(dāng)App進(jìn)入后臺(tái)時(shí),后臺(tái)傳輸就會(huì)被初始化。(需要注意的是iOS8和OS X 10.10之前的版本中后臺(tái)會(huì)話是不支持?jǐn)?shù)據(jù)任務(wù)(data task)的)

會(huì)話的類型都要通過(guò)NSURLSessionConfiguration來(lái)指定
NSURLSessonConfiguration的創(chuàng)建

//默認(rèn)會(huì)話
    NSURLSessionConfiguration* defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
//臨時(shí)會(huì)話
    NSURLSessionConfiguration* ephemeralSessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
//后臺(tái)會(huì)話
    NSURLSessionConfiguration* backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"bacjgroundID"];

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

2 NSURLSession的各種任務(wù)

在一個(gè)Session會(huì)話中可以發(fā)起的任務(wù)分為三種:數(shù)據(jù)任務(wù)(Data task),下載任務(wù)(Download task),上傳任務(wù)(Upload task)。

  • Data Task(數(shù)據(jù)任務(wù))負(fù)責(zé)使用NSData對(duì)象來(lái)發(fā)送和接收數(shù)據(jù)。Data Task是為了那些簡(jiǎn)短的并且經(jīng)常從服務(wù)器請(qǐng)求的數(shù)據(jù)而準(zhǔn)備的。該任務(wù)可以沒(méi)請(qǐng)求一次就對(duì)返回的數(shù)據(jù)進(jìn)行一次處理。
  • Download task(下載任務(wù))以表單的形式接收一個(gè)文件的數(shù)據(jù),該任務(wù)支持后臺(tái)下載。
  • Upload task(上傳任務(wù))以表單的形式上傳一個(gè)文件的數(shù)據(jù),該任務(wù)同樣支持后臺(tái)下載。

二.RUL編碼

1 概述

1.URL編碼概述

無(wú)論是GET、POST還是其他的請(qǐng)求,與服務(wù)器交互的URL是需要進(jìn)行編碼的。因?yàn)檫M(jìn)行URL編碼的參數(shù)服務(wù)器那邊才能進(jìn)行解析,為了能和服務(wù)器正常的交互,我們需要對(duì)我們的參數(shù)進(jìn)行轉(zhuǎn)義和編碼。先簡(jiǎn)單的聊一下什么是URL吧,其實(shí)URL是URI(Uniform Resource Identifier ---- 統(tǒng)一資源定位符)的一種。URL就是互聯(lián)網(wǎng)上資源的地址,用戶可以通過(guò)URL來(lái)找到其想訪問(wèn)的資源。RFC3986文檔規(guī)定,<font color = red>Url中只允許包含英文字母(a-zA-Z)、數(shù)字(0-9)、-_.~4個(gè)特殊字符以及所有保留字符,</font>如果你的URL中含有漢字,那么就需要對(duì)其進(jìn)行轉(zhuǎn)碼了。<font color = red>RFC3986中指定了以下字符為保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]。</font>

在URL編碼時(shí)有一定的規(guī)則,在Get請(qǐng)求中Query是存放在URL后邊,而在POST中是放在Request的Body中。如果你的參數(shù)只是一個(gè)<font color = red>key-Value</font>, 那么Query的形式就是key = value。如果你的參數(shù)是一個(gè)數(shù)組比如<font color = red>key = [itme1, item2, item3,……]</font>,那么你的Query的格式就是<font color = red>key[]=item1&key[itme2]&key[item3]……</font>。如果你的參數(shù)是一個(gè)字典比如<font color = red>key = ["subKey1":"item1", "subKey2":"item2"]</font>, 那么Query對(duì)應(yīng)的形式就是<font color = red>key[subKey1]=item1&key[subKey2]=item2</font>.接下來(lái)我們要做的就是將字典進(jìn)行URL編碼。

2.編碼

AFNetworking中對(duì)應(yīng)的編碼如下:


NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

    if ([value isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = value;
        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }

    return mutableQueryStringComponents;
}

三、數(shù)據(jù)任務(wù)--NSURLSesionDataTask

NSURLSessionDataTask的使用步驟

  • 首先我們先創(chuàng)建會(huì)話使用的URL,在創(chuàng)建URL是我們要對(duì)parameters字典參數(shù)進(jìn)行URL編碼。如果是GET方式的請(qǐng)求的話就使用?號(hào)將我們編碼后的字符串拼接到URL后方即可。
  • 然后創(chuàng)建我們會(huì)話使用的請(qǐng)求(NSURLMutableRequest),在創(chuàng)建請(qǐng)求時(shí)我們要指定請(qǐng)求方式是POST還是GET。如果是POST方式,我們就將編碼后的URL字符串放入request的HTTPBody中即可,有一點(diǎn)需要注意的是我們傳輸?shù)臄?shù)據(jù)都是二進(jìn)制的,所以在將字符串存入HTTPBody之前要將其轉(zhuǎn)換成二進(jìn)制,在轉(zhuǎn)換成二進(jìn)制的同時(shí)我們使用的是UTF8這種編碼格式。
  • 創(chuàng)建完Request后,我們就該創(chuàng)建URLSession了,此處我們?yōu)榱撕?jiǎn)單就獲取了全局的Session單例。我們使用這個(gè)Session單例創(chuàng)建了含有Request對(duì)象的一個(gè)DataTask。在這個(gè)DataTask創(chuàng)建時(shí),有一個(gè)尾隨閉包,這個(gè)尾隨閉包用來(lái)接收服務(wù)器返回來(lái)的數(shù)據(jù)。當(dāng)然此處可以指定代理,使用代理來(lái)接收和解析數(shù)據(jù)的,稍后會(huì)介紹到。
  • 最后切記創(chuàng)建好的Data Task是處于掛起狀態(tài)的,需要你去喚醒它,所以我們要調(diào)用dataTask的resume方法進(jìn)行喚醒。具體如下所示。
    NSString* baseUrlStr = [NSString stringWithUTF8String:"www.baidu.com"];
    
    NSURL* url = [NSURL URLWithString:baseUrlStr];
    
    NSMutableURLRequest* mutableRequest = [NSMutableURLRequest requestWithURL:url];
    
    //設(shè)置請(qǐng)求方式
    mutableRequest.HTTPMethod = @"GET";
    
    //參數(shù)編碼
    
    //拼接參數(shù)
    //get 用 ?
    //post httpbody
    
    
    //創(chuàng)建data task
    NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:mutableRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
    }];
    
    //喚起
    [dataTask resume];
    

四、上傳任務(wù) --- Upload Task

NSURLSessionUploadTask的使用步驟:

  • 先創(chuàng)建URL和request并為request指定請(qǐng)求方式為POST
  • 然后創(chuàng)建Session,此處我們使用SessionConfiguration為Session的類型指定為default session類型。并為該Session指定代理對(duì)象為self。
  • 最后使用Session來(lái)創(chuàng)建upload task,在創(chuàng)建upload task時(shí)為上傳任務(wù)指定NSURLRequest對(duì)象,并且傳入要上傳的表單數(shù)據(jù)formData,當(dāng)然不要忘了將任務(wù)進(jìn)行喚醒。
    NSString* baseUrlStr = [NSString stringWithUTF8String:"www.baidu.com"];
    
    NSURL* url = [NSURL URLWithString:baseUrlStr];
    
    NSMutableURLRequest* mutableRequest = [NSMutableURLRequest requestWithURL:url];
    
    //設(shè)置請(qǐng)求方式
    mutableRequest.HTTPMethod = @"POST";
    //參數(shù)編碼
    
    //拼接參數(shù)
    //post httpbody
    
    //上傳文件的數(shù)據(jù)
    NSString* uploadStr = @"this is a upload str";
    NSData* uploadData = [uploadStr dataUsingEncoding:NSUTF8StringEncoding];
    
    NSURLSessionUploadTask* uploadTask = [self.session uploadTaskWithRequest:mutableRequest fromData:uploadData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    //喚醒
    [uploadTask resume];

備注:
在上傳文件時(shí),如果你想時(shí)刻的監(jiān)聽(tīng)上傳的進(jìn)度,你可以去實(shí)現(xiàn)NSURLSessionTaskDelegate中的didSendBodyData方法,該方法會(huì)實(shí)時(shí)的監(jiān)聽(tīng)文件上傳的速度。

bytesSent回調(diào)參數(shù)表示本次上傳的字節(jié)數(shù),
totalBytesSend回調(diào)參數(shù)表示已經(jīng)上傳的數(shù)據(jù)大小,totalBytesExpectedToSend表示文件公有的大小。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend

五、下載任務(wù)--Download Task

1.創(chuàng)建后臺(tái)會(huì)話

    NSURLSessionConfiguration* backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"bacjgroundID"];
    
    self.session = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration delegate:self delegateQueue:queue];

2.開(kāi)始下載

    NSURLSessionConfiguration* backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"bacjgroundID"];
    
    self.session = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration delegate:self delegateQueue:nil];
    
    
    if (self.resumeData) {
        //如果有已下載的數(shù)據(jù)
        self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            //Block下載方式不適合大文件下載
            
            //接收數(shù)據(jù)
            //確定文件路徑
            //剪切文件
        }];
    }else{
        //沒(méi)下載過(guò)
        NSString* baseUrlStr = [NSString stringWithUTF8String:"www.baidu.com"];
        
        NSURL* url = [NSURL URLWithString:baseUrlStr];
        
        NSMutableURLRequest* mutableRequest = [NSMutableURLRequest requestWithURL:url];
        
        //設(shè)置請(qǐng)求方式
        mutableRequest.HTTPMethod = @"POST";
        //參數(shù)編碼
        
        //拼接參數(shù)
        //post httpbody
        
        //上傳文件的數(shù)據(jù)
        NSString* uploadStr = @"this is a upload str";
        NSData* uploadData = [uploadStr dataUsingEncoding:NSUTF8StringEncoding];
        
        self.downloadTask = [self.session downloadTaskWithRequest:mutableRequest completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            //Block下載方式不適合大文件下載
            
            //接收數(shù)據(jù)
            //確定文件路徑
            //剪切文件
            
        }];
    }
    [self.downloadTask resume];

推薦使用代理方法來(lái)處理下載

3.下載的暫停與恢復(fù)

//取消下載(不可以恢復(fù))
- (void)cancleDownLoad
{
    [self.downloadTask cancel];
}
//可以恢復(fù)下載
- (void)cancleByResumeData
{
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        self.resumeData = resumeData;
    }];
}
//掛起下載
- (void)suspendDownLoad
{
    [self.downloadTask suspend];
}
//回復(fù)繼續(xù)下載
- (void)resumeDownLoad
{
    [self.downloadTask resume];
}

4.下載任務(wù)的回調(diào)

NSURLSessionDownloadDelegate中的方法

/**
 下載完成

 @param session session對(duì)象
 @param downloadTask downloadTask對(duì)象
 @param location 臨時(shí)文件的下載目錄
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    //臨時(shí)文件在下載完成之后如果你不做任何處理的話,那么就會(huì)被自動(dòng)刪除
    
    //創(chuàng)建文件存儲(chǔ)路徑
    //移動(dòng)臨時(shí)文件
}


/**
 下載文件偏移,主要用于斷點(diǎn)續(xù)傳

 @param session session對(duì)象
 @param downloadTask downloadTask對(duì)象
 @param fileOffset 已經(jīng)下載文件的大小
 @param expectedTotalBytes 文件的總大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    //更新下載進(jìn)度條
}



/**
 實(shí)時(shí)監(jiān)聽(tīng)下載任務(wù)回調(diào)

 @param session session對(duì)象
 @param downloadTask downloadTask對(duì)象
 @param bytesWritten 本次下載的數(shù)據(jù)
 @param totalBytesWritten 已經(jīng)下載的數(shù)據(jù)總量
 @param totalBytesExpectedToWrite 文件的總量
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    //更新下載進(jìn)度條
}

//后臺(tái)下載完成之后的操作
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    //配合application:handleEventsForBackgroundURLSession:completionHandler:方法使用
}

//當(dāng)NSURLSession在后臺(tái)開(kāi)啟幾個(gè)任務(wù)之后, 如果有其他幾個(gè)任務(wù)完成后系統(tǒng)會(huì)調(diào)用應(yīng)用程序的
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
    
}

NSURLSessionTaskDelegate中的方法

//任務(wù)完成 不管是不是下載都會(huì)調(diào)用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    
}

六、網(wǎng)絡(luò)緩存

網(wǎng)絡(luò)緩存在網(wǎng)絡(luò)請(qǐng)求中使用的還是蠻多的,尤其是加載一些H5頁(yè)面時(shí)經(jīng)常會(huì)加一些緩存來(lái)提高用戶體驗(yàn)。有時(shí)的一些數(shù)據(jù)也會(huì)進(jìn)行緩存,你可將數(shù)據(jù)緩存到你的SQLite數(shù)據(jù)庫(kù)、PList文件,或者直接使用NSURLSession相關(guān)的東西進(jìn)行緩存。

1、緩存策略

在配置網(wǎng)絡(luò)請(qǐng)求緩存時(shí),有著不同的請(qǐng)求緩存策略。下方就是所有支持的網(wǎng)絡(luò)緩存策略:

  • NSURLRequestUseProtocolCachePolicy -- 緩存存在就讀緩存,若不存在就請(qǐng)求服務(wù)器
  • NSURLRequestReloadIgnoringLocalCacheData -- 忽略緩存,直接請(qǐng)求服務(wù)器數(shù)據(jù)
  • NSURLRequestReturnCacheDataElseLoad -- 本地如有緩存就使用,忽略其有效性,無(wú)則請(qǐng)求服務(wù)器
  • NSURLRequestReturnCacheDataDontLoad -- 直接加載本地緩存,沒(méi)有也不請(qǐng)求網(wǎng)絡(luò)
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData -- 尚未實(shí)現(xiàn)
  • NSURLRequestReloadRevalidatingCacheData -- 尚未實(shí)現(xiàn)

2、NSMutableURLRequest指定緩存策略

mutableRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData

緩存目錄默認(rèn)為~/Library/Caches/[Boundle ID]/fsCachedData/緩存文件

3、NSURLSessionConfiguration指定緩存策略

    SessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;

4、NSURLCache + request進(jìn)行緩存

    NSInteger memoryCapacity = 4 * 1024 * 1024;
    NSInteger diskCapacity = 10 * 1024 * 1024;
    NSString* cacheFilePath = @"myCache";
    
    NSURLCache* urlCache = [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:cacheFilePath];
    [NSURLCache setSharedURLCache:urlCache];

有一點(diǎn)需要注意的是此處設(shè)置的緩存路徑是相對(duì)于/Library/Caches/[Boundle ID]/的

5、NSURLCache +SessionConfiguration進(jìn)行緩存

    SessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
    NSInteger memoryCapacity = 4 * 1024 * 1024;
    NSInteger diskCapacity = 10 * 1024 * 1024;
    NSString* cacheFilePath = @"myCache";
    
    NSURLCache* urlCache = [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:cacheFilePath];
    SessionConfiguration.URLCache = urlCache;

6、清除緩存

NSFileManager removeItemAtPath

七、請(qǐng)求認(rèn)證

1.認(rèn)證方式

  • NSURLAuthenticationMethodHTTPBasic: HTTP基本認(rèn)證,需要提供用戶名和密碼
  • NSURLAuthenticationMethodHTTPDigest: HTTP數(shù)字認(rèn)證,與基本認(rèn)證相似需要用戶名和密碼
  • NSURLAuthenticationMethodHTMLForm: HTML表單認(rèn)證,需要提供用戶名和密碼
  • NSURLAuthenticationMethodNTLM: NTLM認(rèn)證,NTLM(NT LAN Manager)是一系列旨向用戶提供認(rèn)證,完整性和機(jī)密性的微軟安全協(xié)議
  • NSURLAuthenticationMethodNegotiate: 協(xié)商認(rèn)證
  • NSURLAuthenticationMethodClientCertificate: 客戶端認(rèn)證,需要客戶端提供認(rèn)證所需的證書(shū)
  • NSURLAuthenticationMethodServerTrust: 服務(wù)端認(rèn)證,由認(rèn)證請(qǐng)求的保護(hù)空間提供信任

后兩個(gè)就是我們?cè)谡?qǐng)求HTTPS時(shí)會(huì)遇到的認(rèn)證,需要服務(wù)器或者客戶端來(lái)提供認(rèn)證的,這個(gè)證書(shū)就是我們平時(shí)常說(shuō)的CA證書(shū).

2.認(rèn)證處理策略

當(dāng)我們進(jìn)行網(wǎng)絡(luò)求時(shí),會(huì)對(duì)相應(yīng)的認(rèn)證做出響應(yīng)。在NSURLSession進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)支持四種證書(shū)處理策略,這些認(rèn)證處理策略以枚舉的形式來(lái)存儲(chǔ),枚舉的類型為NSURLSessionAuthChallengeDisposition。下方就是認(rèn)證的所有處理策略:

  • NSURLSessionAuthChallengeUseCredential 使用證書(shū)
  • NSURLSessionAuthChallengePerformDefaultHandling 執(zhí)行默認(rèn)處理, 類似于該代理沒(méi)有被實(shí)現(xiàn)一樣,credential參數(shù)會(huì)被忽略
  • NSURLSessionAuthChallengeCancelAuthenticationChallenge 取消請(qǐng)求,credential參數(shù)同樣會(huì)被忽略
  • NSURLSessionAuthChallengeRejectProtectionSpace 拒絕保護(hù)空間,重試下一次認(rèn)證,credential參數(shù)同樣會(huì)被忽略

3.HTTPS請(qǐng)求證書(shū)處理

發(fā)起上述https請(qǐng)求后,就會(huì)執(zhí)行下方的代理方法。下方的委托代理方法屬于NSURLSessionDelegate中處理認(rèn)證的方法,也就是如果服務(wù)器需要認(rèn)證時(shí)就會(huì)執(zhí)行下方的回調(diào)方法。下方代碼首先從授權(quán)質(zhì)疑的保護(hù)空間中取出認(rèn)證方式,然后根據(jù)不同的認(rèn)證方式進(jìn)行不同的處理。下方給出了兩種認(rèn)證方式的處理,上面的if語(yǔ)句塊賦值服務(wù)端認(rèn)證,下面的if語(yǔ)句塊負(fù)責(zé)HTTP的基本認(rèn)證。具體處理方式如下所示。有一點(diǎn)需要注意的是如果在該委托回調(diào)方法中如果不執(zhí)行completionHandler閉包,那么認(rèn)證就會(huì)失效,是請(qǐng)求不到數(shù)據(jù)的。


/**
 服務(wù)器需要驗(yàn)證時(shí)會(huì)觸發(fā)

 @param session session對(duì)象
 @param challenge 授權(quán)質(zhì)疑
 @param completionHandler block
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
    
    // 判斷是否是信任服務(wù)器證書(shū)
    if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        //https
        // 告訴服務(wù)器,客戶端信任證書(shū)
        // 創(chuàng)建憑據(jù)對(duì)象
        NSURLCredential *credntial = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        // 通過(guò)completionHandler告訴服務(wù)器信任證書(shū)
        completionHandler(NSURLSessionAuthChallengeUseCredential,credntial);
    }
    /**
      * HTTP BASE
     */
    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) {
        NSURLCredential *credntial = [[NSURLCredential alloc] initWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceForSession];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credntial);
    }
    //取消請(qǐng)求
    completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
}

八、NSURLSession相關(guān)代理

1、NSURLSessionDelegate

  1. didReceiveChallenge
    其他
//Session無(wú)效后被調(diào)用
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
{
    
}
//后臺(tái)Session在執(zhí)行完后臺(tái)任務(wù)后所執(zhí)行的方法
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    
}

2、NSURLSessionTaskDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;
{
    
}

3、NSURLSessionDataDelegate

NSURLSessionDataDelegate中的方法主要是用來(lái)處理Data Task任務(wù)相應(yīng)的事件的

1、相應(yīng)處理策略

  • NSURLSessionResponseCancel 取消數(shù)據(jù)的加載,默認(rèn)為 Cancel。此處理方式就是忽略數(shù)據(jù)的加載,取消對(duì)響應(yīng)數(shù)據(jù)的進(jìn)一步解析
  • NSURLSessionResponseAllow 允許繼續(xù)操作, 會(huì)執(zhí)行 NSURLSessionDataDelegate中的dataTaskDidReceiveData回調(diào)方法
  • NSURLSessionResponseBecomeDownload 將Data Task的響應(yīng)轉(zhuǎn)變?yōu)镈ownloadTask,會(huì)執(zhí)行NSURLSessionDownloadDelegate代理中相應(yīng)的方法
  • NSURLSessionResponseBecomeStream 將Data Task的響應(yīng)轉(zhuǎn)變?yōu)镈ownloadTask,會(huì)執(zhí)行NSURLSessionStreamDelegate代理中相應(yīng)的方法

2.代理方法

/**
 收到響應(yīng)時(shí)會(huì)執(zhí)行的方法

 @param session session
 @param dataTask dataTask
 @param response 服務(wù)器響應(yīng)
 @param completionHandler block
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    //處理策略設(shè)置成Allow后會(huì)執(zhí)行下方的方法,如果響應(yīng)處理策略不是Allow那么就不會(huì)接收到服務(wù)器的Data
}

/**
 接收數(shù)據(jù)后會(huì)執(zhí)行的方法
 
 @param session session
 @param dataTask dataTask
 @param data 接收的數(shù)據(jù)
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    
}

/**
 任務(wù)轉(zhuǎn)變?yōu)橄螺d時(shí)所執(zhí)行的方法

 @param session session
 @param dataTask dataTask
 @param downloadTask downloadTask
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    
}

/**
 任務(wù)轉(zhuǎn)變?yōu)閟treamTask時(shí)所執(zhí)行的方法

 @param session session
 @param dataTask dataTask
 @param streamTask streamTask
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask
{
    //iOS9
}


/**
 將要進(jìn)行緩存時(shí)執(zhí)行的方法

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

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

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