iOS學習--uexiASIHTTPRequest使用指南---

ASIHTTPRequest使用指南---<<翻譯稿>>

當第一次使用ASIHTTPRequest進行http請求時,會出現非常多的bug提示.查了一些資料,發現在少倒入了幾個資源包:大概是:

CFNetwork.framework;

SystemConfiguration.framework;

MobileCoreServices.framework.


原文:http://allseeing-i.com/ASIHTTPRequest/How-to-use
Creating and running requests
Creating a synchronous request

The simplest way to use ASIHTTPRequest. Sending the startSynchronous message will execute the request in the same thread, and return control when it has completed (successfully or otherwise).

Check for problems by inspecting the error property.

To get the response as a string, call the responseString method. Don’t use this for binary data - use responseData to get an NSData object, or, for larger files, set your request to download to a file with the downloadDestinationPath property.

- (IBAction)grabURL:(id)sender
{
  NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  [request startSynchronous];
  NSError *error = [request error];
  if (!error) {
    NSString *response = [request responseString];
  }
}

In general, you should use asynchronous requests in preference to synchronous requests. When you use ASIHTTPRequest synchronously from the main thread, your application’s user interface will lock up and become unusable for the duration of the request.

譯文:

創建和運行請求

創建一個同步請求

這是最簡單的用法,發送 startSynchronous 消息將在相同線程中執行請求,不管是否成功,完成後返回控制。
查看error屬性以檢測問題。
要以字符串形式得到響應,就調用responseString方法。這個方法不適合二進制數據 - 你應該使用responseData 得到NSData對象,或者如果有更大的文件,你可以設置downloadDestinationPath將請求下載到文件。


- (IBAction)grabURL:(id)sender
{
  NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  [request startSynchronous];
  NSError *error = [request error];
  if (!error) {
    NSString *response = [request responseString];
  }
}

注意:一般的,你應該優先使用異步請求,如果你在主線程中使用ASIHTTPRequest的同步方法,程序的ui在請求過程中將被鎖定而無法響應。

 

 

創建一個異步請求

下面的代碼做同樣的事情,但請求運行於後臺。

- (IBAction)grabURLInBackground:(id)sender
{
   NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   [request setDelegate:self];
   [request startAsynchronous];
}

- (void)requestFinished:(ASIHTTPRequest *)request
{
   // Use when fetching text data
   NSString *responseString = [request responseString];

   // Use when fetching binary data
   NSData *responseData = [request responseData];
}

- (void)requestFailed:(ASIHTTPRequest *)request
{
   NSError *error = [request error];
}

注意我們設置請求的委託,這樣就能在請求完成或失敗時得到通知。
這是創建異步請求最簡單的方式,他會運行於場景後面的一個全局的NSOperationQueue中,對於更復雜的操作,例如在多個請求中追蹤進展,你可能想要創建自己的隊列,下面我們談談這個。

 

 

 

使用程序塊(blocks)
對於v1.8,我們可以在支持程序塊的平臺使用他們:

- (IBAction)grabURLInBackground:(id)sender
{
   NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
   __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   [request setCompletionBlock:^{
      // Use when fetching text data
      NSString *responseString = [request responseString];

      // Use when fetching binary data
      NSData *responseData = [request responseData];
   }];
   [request setFailedBlock:^{
      NSError *error = [request error];
   }];
   [request startAsynchronous];
}

注意:在我們聲明請求時,用到了__block限定語,這很重要!它告訴block不要保留請求,來防止一個保留循環(retain-cycle),因爲請求總會保留block.

 

 

使用隊列

這個例子做同樣的事,不同的是,我們爲自己的請求創建了NSOperationQueue。

使用一個NSOperationQueue或者ASINetworkQueue,你能更有效的控制異步請求。我們使用一個請求,只有一定數量的請求能同時運行。如果你添加多於maxConcurrentOperationCount數量的請求,這些請求將等到其他請求完成後纔會開始。

- (IBAction)grabURLInTheBackground:(id)sender
{
   if (![self queue]) {
      [self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
   }

   NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   [request setDelegate:self];
   [request setDidFinishSelector:@selector(requestDone:)];
   [request setDidFailSelector:@selector(requestWentWrong:)];
   [[self queue] addOperation:request]; //queue is an NSOperationQueue
}

- (void)requestDone:(ASIHTTPRequest *)request
{
   NSString *response = [request responseString];
}

- (void)requestWentWrong:(ASIHTTPRequest *)request
{
   NSError *error = [request error];
}


在上面的例子中,'queue'是控制器的一個保留的NSOperationQueue屬性。

我們設置自定義的selector處理成功或失敗的回調。如果你不設置這些,默認的requestFinished和requestFailed將被使用,就像在之前的例子中一樣。

在委託方法中處理對歌請求的成功和失敗

如果你需要處理許多不同類型的請求,你有幾個選擇:

1. 如果你的請求都屬於一個大的範圍,你又想區別他們,可以設置請求的userInfo字典屬性,填入你的自定義數據,你就可以在委託方法中讀取了。在更簡單的用例中,你可以簡單的設置tag屬性。這些屬性都是給你自己用的,不會發送到服務器。

2. 如果你處理完全不同的請求,爲每個請求設置不同的 setDidFinishSelector / setDidFailSelector。

3. 對更復雜的情況,或者你想在後臺解析響應數據,可以爲每個請求類型創建一個ASIHTTPRequest的子類,然後重載requestFinished: 和 failWithError: 。

注意:最好避免在委託方法中,用url來區分不同的請求,因爲url屬性在重定向時會改變。如果你真的真的想用的話,請用[request originalURL] 代替,這個將總是記錄第請求連接時的首個url。

 

 

關於ASINetworkQueues

ASINetworkQueues是NSOperationQueue,它提供了額外的功能。
它的主要目的是追蹤整個隊列上傳或下載的進度。
ASINetworkQueues提供了一些額外的委託方法selector。
requestDidStartSelector
每當隊列中的一個請求開始運行時調用。你可以使用這個作爲didStartSelector的替代方法,爲你加到隊列的請求設置一個委託。

requestDidReceiveResponseHeadersSelector
每當隊列中的一個請求從服務器得到響應頭時調用。對於大的下載,這個 selector有時在請求實際完成前執行。
你可以使用它作爲對didReceiveResponseHeadersSelector的替代。

requestDidFinishSelector
每當隊列中的一個請求完成時調用。可以使用它作爲對didFinishSelector的替代。

requestDidFailSelector
每當隊列中的請求失敗時調用,可作爲didFailSelector的替代。

queueDidFinishSelector
當隊列完成時調用,不管單個請求成功或失敗。

使用以上selector,要將隊列的委託,而不是請求的委託,設置到實現這些selector所代表的方法的控制器。

ASINetworkQueues工作起來和NSOperationQueues稍有不同,加到其中的請求不會立刻運行,當使用ASINetworkQueue時,添加你想運行的所有請求,然後調用[queue go]。當你啓動一個隊列,將精確進度(accurate progress)打開時,他會首先爲隊列中所有的get請求執行一個head請求,來得到將要下載數據的總體尺寸。獲得此數據後,它就能準確的顯示總體進度,然後真實的請求才會開始。
問題:當你向一個運行中的ASINetworkQueue加入一個請求時發生了什麼事情?
回答:如果你使用ASINetworkQueue來追蹤幾個請求的總體進度,整體進度只會在哪個請求開始執行時,纔會將它計算在內。ASINetworkQueue不會在運行中,當加入請求後執行head請求,所以如果你立刻向運行的隊列加入許多請求,總體進度不會立刻更新。
如果隊列已經運行,你不需要再次調用[queue go]。

當ASINetworkQueue中的一個請求失敗,隊列默認的將取消所有其他的請求。你可以調用[queue setShouldCancelAllRequestsOnFailure:NO].來關閉這一行爲。
ASINetworkQueues只能執行ASIHTTPRequest操作,而不能用於一般操作,嘗試添加一個非ASIHTTPRequest的NSOperation將產生一個異常。
提示:這裏有一個創建和使用ASINetworkQueue的完整例子:
http://gist.github.com/150447


補充內容 (2011-9-27 16:42):
未完待續。。。

 

 

取消一個異步請求
爲了取消一個異步請求,可以調用[request cancel],不管該請求是用[request startAsynchronous] 啓動的,還是在你創建的隊列中運行。注意你不能取消一個同步請求。
注意,當你取消一個請求,請求會將之視爲一個錯誤,然後會調用你的委託或者隊列的失敗委託方法。如果你不想這種事發生,在取消之前將委託設爲nil,或者使用clearDelegatesAndCancel作爲代替。

// Cancels an asynchronous request
[request cancel]

// Cancels an asynchronous request, clearing all delegates and blocks first
[request clearDelegatesAndCancel];

當使用一個ASINetworkQueue時,你取消其中一個請求,所有其他的請求也會被取消,除非隊列的shouldCancelAllRequestsOnFailure爲no, 默認爲yes。

// When a request in this queue fails or is cancelled, other requests will continue to run
[queue setShouldCancelAllRequestsOnFailure:NO];

// Cancel all requests in a queue
[queue cancelAllOperations];

 

 

安全處理委託在請求完成前釋放的情況

請求不會保留他們的委託,所以如果你的委託有機會在請求運行時釋放的話,你能及時清理請求的委託屬性是至關重要的。在大多數情況下,如果你的委託將要釋放,你大概也想取消請求,因爲你不再關心請求的狀態。

在下面的例子中,控制器有一個保存於保留實例變量中的ASIHTTPRequest對象。我們在其dealloc實現中調用clearDelegatesAndCancel方法,在我們釋放請求的引用之前:

// Ddealloc method for our controller
- (void)dealloc
{
   [request clearDelegatesAndCancel];
   [request release];
   ...
   [super dealloc];
}

 

 

 

put方法和自定義post
如果你想通過put發送數據,或者想要發送post,但是想自己創建post數據體(body),使用appendPostData: 或者 appendPostDataFromFile:。

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request appendPostData:[@"This is my data" dataUsingEncoding:NSUTF8StringEncoding]];
// Default becomes POST when you use appendPostData: / appendPostDataFromFile: / setPostBody:
[request setRequestMethod:@"PUT"];

如果你想發送大量的數據,並且不用ASIFormDataRequest,參看後面的‘從磁盤以流式post數據’小節。

 

 

下載數據

將響應數據直接下載爲文件

如果你請求的數據相當大,你可以直接將下載內存直接保存爲文件。這樣,ASIHTTPRequest不需要一次在內存中保存整個請求。

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadDestinationPath:@"/Users/ben/Desktop/my_file.txt"];

當使用downloadDestinationPath下載到文件時,下載過程中,數據將被保存在一個臨時文件中。這個文件路徑保存於temporaryFileDownloadPath。當請求成功完成,下面的事情之一會發生:

如果數據時gzip壓縮過的,壓縮文件將被解壓到downloadDestinationPath,並且臨時文件被刪除;
如果數據沒被壓縮,臨時文件移動到downloadDestinationPath,覆蓋掉任何之前的文件。

注意:如果響應body是空的,文件是不會被創建的。所以如果請求可能返回一個空的body,你要確定在嘗試操作文件之前,檢查它是否存在。

 

 

處理收到的響應數據

如果你需要處理收到的響應數據,比如你想使用流解析器(streaming parser)來解析仍在下載的響應數據,就需要在委託中實現request:didReceiveData:方法。注意當你這麼做時,ASIHTTPRequest不會產生responseData,也不會將數據寫入downloadDestinationPath - 你必須自己搞定保存響應數據的事。

讀取HTTP狀態碼

ASIHTTPRequest 不會鳥大多數的http狀態碼,重定向和驗證的狀態碼除外。所以,就要靠你檢查問題,比如404錯誤,然後確定你做了恰當的處理。

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
int statusCode = [request responseStatusCode];
NSString *statusMessage = [request responseStatusMessage];


讀取響應頭

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSString *poweredBy = [[request responseHeaders] objectForKey:@"X-Powered-By"];
NSString *contentType = [[request responseHeaders] objectForKey:@"Content-Type"];


處理文本編碼
ASIHTTPRequest會嘗試從Content-Type頭得到數據的編碼。如果它發現了一個編碼類型,他會社子responseEncoding爲相應的NSStringEncoding。如果它沒在頭部發現編碼信息,就會用defaultResponseEncoding,這個值默認爲NSISOLatin1StringEncoding。
當你調用[request responseString],ASIHTTPRequest將嘗試用responseEncoding作爲源編碼,從收到的數據創建一個字符串。

處理重定向
當ASIHTTPRequest碰到以下http狀態碼時,會自動重定向到一個新的url,假設Location頭已經發送:
301 永久移動
302 發現
303 參看其他
當重定向發生,響應數據(responseHeaders / responseCookies / responseData / responseString 等) 的值將反映從最終位置收到的內容。
在重定向週期中碰到的任何url上設置的Cookie,都倍存儲到全局cookie倉庫,在恰當的時候會由重定向請求呈現給服務器。
你可以關閉自動重定向,設置shouldRedirect屬性爲no即可。

注意:默認情況下,自動重定向總是以get方式進行請求(沒有body)。這個行爲複合大多數的瀏覽器,除了象301和302這樣應該用原有方式重定向的規範。
爲了保留301和302的原有方式(包含請求的body),需要將shouldUseRFC2616RedirectBehaviour設置爲yes,在你開始請求之前。

補充內容 (2011-9-27 19:50):
文檔真的很長啊。。。才翻譯了1/4,今天歇菜,明天繼續。。。

 

 

跟蹤進度

每個ASIHTTPRequest都有兩個用於跟蹤進度的委託 - downloadProgressDelegate用於下載,uploadProgressDelegate用於上傳。
進度委託可以是NSProgressIndicators(Mac OS X)或者UIProgressViews(iPhone)。ASIHTTPRequest會自動適應這兩者行爲上的差異。你也能夠使用自定義的類作爲進度委託,只要它響應setProgress:方法。

如果你正執行單個請求,在這個請求上設置一個上傳或下載委託。
如果你在隊列中執行多個請求,並且想跟蹤隊列中所有請求的總體進度,可以使用ASINetworkQueue並設置隊列的進度委託
如果想同時做上面兩件事,也是可行的。

重點注意:如果你正在向需要驗證的站點進行上傳操作,爲了提供有效驗證,每當上傳失敗時,進度會被重設爲之前的值。爲此,如果你正同驗證web服務器通信,建議你僅當useSessionPersistence爲yes時使用上傳進度委託,並確保你在嘗試跟蹤大量上傳數據前,在另一個請求中驗證。

當請求body小於128kb時,目前是不能夠跟蹤上傳進度的。對請求大於128kb的請求,進度委託不會收到第一個128kb的post數據進度的信息。這是因爲CFNetwork API的限製造成的。
2009.6.21日更新:apple的好傢伙非常友好的定位了我的bug報告!在iphone 2.0 sdk中,貌似這個緩衝尺寸減小到了32kb,這讓精確的上傳進度跟蹤更加可靠了。

 

 

跟蹤單個請求的下載進度

在這個例子中,myProgressIndicator是一個NSProgressIndicator。

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadProgressDelegate:myProgressIndicator];
[request startSynchronous];
NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);

跟蹤一組請求的下載進度

這個例子中,myProgressIndicator 是一個UIProgressView,myQueue是一個ASINetworkQueue。
- (void)fetchThisURLFiveTimes:(NSURL *)url
{
   [myQueue cancelAllOperations];
   [myQueue setDownloadProgressDelegate:myProgressIndicator];
   [myQueue setDelegate:self];
   [myQueue setRequestDidFinishSelector:@selector(queueComplete:)];
   int i;
   for (i=0; i<5; i++) {
      ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
      [myQueue addOperation:request];
   }
   [myQueue go];
}

- (void)queueComplete:(ASINetworkQueue *)queue
{
   NSLog(@"Value: %f", [myProgressIndicator progress]);
}

注意對於ASINetworkQueues,我們必須調用[myQueue go]來啓動隊列。

跟蹤單個請求的上傳進度

本例中,myProgressIndicator是一個UIProgressView。
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"Ben" forKey:@"first_name"];
[request setPostValue:@"Copsey" forKey:@"last_name"];
[request setUploadProgressDelegate:myProgressIndicator];
[request startSynchronous];
NSLog(@"Value: %f",[myProgressIndicator progress]);

跟蹤一組請求的上傳進度

本例中,myProgressIndicator是一個NSProgressUbdicator,myQueue是一個ASINetworkQueue。
- (void)uploadSomethingFiveTimes:(NSURL *)url
{
   [myQueue cancelAllOperations];
   [myQueue setUploadProgressDelegate:myProgressIndicator];
   [myQueue setDelegate:self];
   [myQueue setRequestDidFinishSelector:@selector(queueComplete:)];
   int i;
   for (i=0; i<5; i++) {
      ASIHTTPRequest *request = [ASIFormDataRequest requestWithURL:url];
      [request setPostBody:[@"Some data" dataUsingEncoding:NSUTF8StringEncoding]];
      [myQueue addOperation:request];
   }
   [myQueue go];
}

- (void)queueComplete:(ASINetworkQueue *)queue
{
   NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
}

 

 

精確進度vs簡單進度

ASIHTTPRequest提供了兩種顯示進度的途徑,簡單進度 和 精確進度。他們由ASIHTTPRequests 和 ASINetworkQueues的showAccurateProgress來控制。如果你在一個請求上設置showAccurateProgress,只會影響這個請求。如果你設置了隊列,將影響到隊列的全部請求。

簡單請求
當你使用簡單進度,進度僅在請求完成時更新。對於單個進程,你只能得到0%和100%完成。對於一個包括4個請求的隊列,你能得到5次進度更新,0%,25%,50%,75%和100%,每一次增量表明有一個請求完成了。
簡單進度(showAccurateProgress = NO)是ASINetworkQueues的默認值,它很好的適用於包含大量的輕量級上傳/下載請求的隊列。

精確進度
使用精確進度時,進度以收發的字節來更新,所以極適合收發大量數據的請求,它會更好的指示一個耗時請求收發了多少數據。
使用精確進度會稍稍降低上傳操作的性能,因爲進度委託(可能是UIProgressView或者NSProgressIndicator)將更爲頻繁的重繪。
使用精確進度會對使用隊列的下載任務影響更大,因爲隊列在下載前,會先爲其中的get請求執行head請求,來決定將要下載的數據總體尺寸。強烈推薦你在隊列中下載大文件時使用精確進度,但是應該避免隊列中包含大量小型下載時使用它。
精確進度(showAccurateProgress = YES)是ASIHTTPRequests執行同步任務時的默認值。

 

 

自定義進度跟蹤

ASIProgressDelegate協議定義了得到請求的更新進度的委託的所有方法。大多數情況下,爲NSProgressIndicator或UIProgressView設置uploadProgressDelegate 或 downloadProgressDelegate就足夠了。但是,如果你想做更復雜的進度跟蹤,你的進度委託應該優先於setProgress: (iOS) 或者setDoubleValue: / setMaxValue: (Mac)實現下列方法。這些方法允許你得到收發的實際字節數,而不是更簡單的方法得到的介於0和1之間的數字。
downloadProgressDelegates的方法

request:didReceiveBytes:每當請求下載更多數據時在downloadProgressDelegate上調用。注意這有別於一般會實現的request:didReceiveData:委託。
request:incrementDownloadSizeBy:下載尺寸改變時被調用,傳入的參數是下載尺寸的增量。一般發生在請求收到響應頭並獲得了下載的尺寸時。

uploadProgressDelegates的方法

request:didSendBytes: 每當請求能發送一些數據時在uploadProgressDelegate上調用。重點提醒:這個方法能被小於0的數字調用,當一個請求需要刪除上傳進度時(一般是當它已經上傳了數據,但是驗證失敗或者由於某種原因需要再次運行時)。
request:incrementUploadSizeBy: 上傳尺寸改變時調用。傳入的尺寸經常會小於0,由於請求調整了上傳尺寸,它將os內部的緩衝尺寸也計算在內了。

 

處理http驗證

如果你正在連接到需要驗證的服務器,你大概想要看看這個流程圖
http://allseeing-i.com/ASIHTTPRe ... ticationProcess.pdf
,演示了ASIHTTPRequest如何找到和應用請求驗證。

在url中指定用戶名和密碼
NSURL *url = [NSURL URLWithString:@"http://username:[email protected]/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

設置請求的用戶名和密碼
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];

 

 

在鍵鏈(keychain)中儲存證書
如果你打開了keychainPersistence,任何你提供的有效用戶名和密碼將被存儲到鍵鏈中。隨後的請求會重用鍵鏈中的用戶名和密碼,即使你退出再重啓應用。
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseKeychainPersistence:YES];
[request setUsername:@"username"];
[request setPassword:@"password"];

如果你使用鍵鏈同時希望自己管理它,你應該能在ASIHTTPRequest.h中找到有助於此與鍵鏈相關的類方法。

 

 

在會話中保存證書

如果useSessionPersistence打開了,默認是打開的,ASIHTTPRequest將證書保存到內存,並可以在後續請求中重用他們。
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];
[request setUseSessionPersistence:YES]; //Shouldn't be needed as this is the default

//Should reuse our username and password
request = [ASIHTTPRequest requestWithURL:url];

 

NTML驗證
要通過使用ntml方案的windows服務器進行驗證,你還需要指定你驗證的域(domain)。
NSURL *url = [NSURL URLWithString:@"http://my.windows.server/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];
[request setDomain:@"my-domain"];

使用委託來提供證書
相對於提前指定驗證證書,你可能更願意在請求不能從會話驗證緩存或鍵鏈中得到證書時,向它的委託詢問。這可能有助於你希望連到一個服務器,但你不確定它需要哪種類型的驗證。
確定你的委託實現了authenticationNeededForRequest,ASIHTTPRequest會暫停一個請求,當它等待一個委託以獲得將要使用的證書時。當你有你需要的證書時,只要在請求上設置他們,然後調用[request retryUsingSuppliedCredentials],如果你想取消,就要調用[request cancelAuthentication],這也會取消請求。

對於v1.0.8,請求的委託一次只會收到一個authenticationNeededForRequest或者proxyAuthenticationNeededForRequest。當委託處理第一個請求時,其他需要驗證的請求會暫停執行。如果證書通過驗證,任何當前進程的其他請求都會嘗試重用他們,假設他們對url有效。如果委託取消了驗證,且隊列的shouldCancelAllRequestsOnFailure爲yes,所有其他的請求不再嘗試查詢證書而會取消。

在使用同步請求時,你不能使用代理模式進行驗證。
在老版本中這會導致應用掛起,對於v1.0.8代理方法不再被調用。

 

 

使用內置驗證對話框(目前僅ios可用)

在v1.0.8中新增了ASIAuthenticationDialog類。它主要用來和驗證代理一同工作,但是它可以被用來爲驗證web服務器詢問用戶的證書。
爲了最好的用戶體驗,多數連到單個服務的應用應該在其委託中實現authenticationNeededForRequest:,或者避免完全的使用委託樣式(delegate-style)驗證。但是有時爲常規驗證而使用ASIHTTPRequest的標準驗證對話框有些好處:

你不想創建你自己的登錄表單
你大概需要從外部來源獲得數據,而不太確定它需不需要驗證。

爲此,在請求中設置shouldPresentAuthenticationDialog爲true,如果你的委託沒有實現authenticationNeededForRequest,用戶將看到此對話框。

驗證對話框不會在同步請求時出現。

對話框有點模仿了iphone上的safari,幷包括:
一條消息指明這個認證是爲web服務器做的(而不是代理)
你連接到的服務器的主機名或ip
驗證域(authentication realm),如果支持的話
輸入用戶名和密碼的文本框
當連接到一個使用ntlm方案的服務器時,對話框也包括一個輸入域(domain)的文本框
關於驗證是否以明文(plain text)發送的提示(僅在用無ssl的基本驗證時才被明文發送)。

如果你想改變對話框的外觀,子類化ASIHTTPRequest,重載showAuthenticationDialog來顯示你自己的自定義對話框或者子類化ASIAuthenticationDialog。

 

 

在服務器要求之前提供證書

非常重要:在v1.0.8中,使用基本驗證的請求時,這個特徵改變了,你可能需要更新你的代碼。

ASIHTTPRequest可以在首次請求時就爲服務器提供證書,而不是等到服務器要求證書。結果是使用驗證時獲得更好的性能,因爲它避免了額外的請求。

使用基本驗證來觸發此行爲時,你應該手動設置請求的authenticationScheme:
[request setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic];

使用其他驗證方案時,證書可以在服務器要求之前提供,但只有在另一個請求成功通過此服務器驗證之後。

你可能希望禁用此特徵,如果:
你的應用可能使用多組證書來同時和同一個服務器對話。
你的應用是安全至上的,使用這個特性本身不太安全,因爲在你有機會驗證你連接到你希望連接的服務器之前,證書已經被髮送了。

爲了禁用此特徵,使用以下代碼:
[request setShouldPresentCredentialsBeforeChallenge:NO];

 

Cookies

持久化cookies

ASIHTTPRequest允許你使用全局存儲,全局存儲爲所有max os上使用CFNetwork或NSURLRequest API的應用所共享。如果useCookiePersistence爲on(默認如此),cookies將被保存到共享的NSHTTPCookieStorage容器中,並自動被其他請求自動重用。如果cookie僅對特定請求有效,ASIHTTPRequest提供其他程序創建的cookie是沒有價值的。
你可以象這樣清除在會話中創建的所有cookie:
[ASIHTTPRequest setSessionCookies:nil];

這裏,'會話cookie'指的一個會話中創建的所有cookie,而不是程序退出時被刪除的無過期時間的cookie(經常被稱爲會話cookie)。

另外,類的方便(convenience)方法clearSession將清除所有會話中創建的cookie,以及任何緩存的驗證數據。


手工處理cookie
如果你喜歡,你可以關掉useCookiePersistence,然後手動管理特定請求的一組cookie。

//Create a cookie
NSDictionary *properties = [[[NSMutableDictionary alloc] init] autorelease];
[properties setValue:[@"Test Value" encodedCookieValue] forKey:NSHTTPCookieValue];
[properties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName];
[properties setValue:@".allseeing-i.com" forKey:NSHTTPCookieDomain];
[properties setValue:[NSDate dateWithTimeIntervalSinceNow:60*60] forKey:NSHTTPCookieExpires];
[properties setValue:@"/asi-http-request/tests" forKey:NSHTTPCookiePath];
NSHTTPCookie *cookie = [[[NSHTTPCookie alloc] initWithProperties:properties] autorelease];
 
//This url will return the value of the 'ASIHTTPRequestTestCookie' cookie
url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"];
request = [ASIHTTPRequest requestWithURL:url];
[request setUseCookiePersistence:NO];
[request setRequestCookies:[NSMutableArray arrayWithObject:cookie]];
[request startSynchronous];
 
//Should be: I have 'Test Value' as the value of 'ASIHTTPRequestTestCookie'
NSLog(@"%@",[request responseString]);

 

 

處理壓縮的響應,以及壓縮請求body

使用gzip來處理壓縮的響應數據

對於v0.9,ASIHTTPRequest不會提醒服務器它可以接受gzip壓縮格式數據。如果你在現有項目中升級ASIHTTPRequest,參看設置指令地址
http://allseeing-i.com/ASIHTTPRequest/Setup-instructions
瞭解如何鏈接到zlib。

許多web服務器可以在發送數據前進行壓縮 - 這樣可以加快下載和降低使用帶寬,代價是服務器(用以壓縮數據)和客戶端(解壓數據)增加了額外的cpu時間。一般說來,只有某些類型的數據會被壓縮 - 許多二進制格式例如jpeg,gif,png,swf和pdf已經壓縮過他們的數據,所以gzip壓縮不會用於發送他們到客戶端。文本文件如網頁和xml文檔是gzip壓縮的完美候選人,因爲他們經常包含大量重複信息。

如何設置apache使用mod_deflate來壓縮數據
apatche 2.x 之後帶來了mod_deflate擴展允許透明的壓縮某些類型的數據。要打開他,你需要在apatche配置文件中啓用mode_deflate,並添加mod_deflate指令到你的虛擬主機配置中,或者到你的.htaccess文件中。

在ASIHTTPRequest中使用gzip

- (IBAction)grabURL:(id)sender
{
  NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  // YES is the default, you can turn off gzip compression by setting this to NO
  [request setAllowCompressedResponse:YES];
  [request startSynchronous];
  BOOL *dataWasCompressed = [request isResponseCompressed]; // Was the response gzip compressed?
  NSData *compressedResponse = [request rawResponseData]; // Compressed data
  NSData *uncompressedData = [request responseData]; // Uncompressed data
  NSString *response = [request responseString]; // Uncompressed data as a string
}


當allowCompressedResponse爲真,ASIHTTPRequest將添加Accept-Encoding頭到請求中,表明我們可以接受gzip壓縮的響應數據。如果響應頭包含一個content-encoding頭指明瞭數據已被壓縮,調用responseData 或者 responseString將在返回前解壓數據。你可以通過調用rawResponseData得到原始壓縮數據。

聯機解壓gzip響應包
默認的,ASIHTTPRequest會一直等到請求完成對gzip響應包的解壓縮。設置shouldWaitToInflateCompressedResponses屬性爲no,你可以告訴ASIHTTPRequest在收到數據同時進行解壓。有時候,這會提升一點速度,因爲當一個請求等待更多響應時,數據可以同時被處理。
這個特性對於需要使用流解析器(streaming parser)如xml或json解析器時尤其有用,打開這個選項,你可以直接給你的解析器輸送解壓過的數據,他們來自你委託實現的request:didReceiveData: 方法。

注意當shouldWaitToInflateCompressedResponses爲no時,原始(壓縮過的)數據將被丟棄,參看ASIHTTPRequest.h的註釋獲得更多信息。


使用gzip來壓縮請求body
v1.0.3新增了請求body的gzip壓縮。使用這個特性,你的應用可以壓縮post/put操作的內容,只要設置shouldCompressRequestBody爲yes即可。shouldCompressRequestBody默認爲no。

當你配置了SetInputFilter DEFLATE 後,apatche的mod_deflate可以自動的解壓gzip請求體。這個方法爲cgi內容工作,但是如果你使用apatche模塊構建成了一個RESOURCE過濾器(如mod PHP)則不能工作,這時你需要自己搞定數據解壓。

注意:ASIHTTPRequest不能檢查到服務器是否接受gzip請求體。只有當你確定服務器能搞定gzip body時使用這項特性。
避免對壓縮格式文件如jpeg/png/gif/pdf/swf使用gzip,你會發現gzip版本會比原始文件大。

 

恢復被打斷的下載
自v0.94開始,ASIHTTPRequest能夠恢復不完整的下載
- (IBAction)resumeInterruptedDownload:(id)sender
{
  NSURL *url = [NSURL URLWithString:
    @"http://allseeing-i.com/ASIHTTPRequest/Tests/the_great_american_novel.txt"];
  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
 
  NSString *downloadPath = @"/Users/ben/Desktop/my_work_in_progress.txt";
 
  // The full file will be moved here if and when the request completes successfully
  [request setDownloadDestinationPath:downloadPath];
 
  // This file has part of the download in it already
  [request setTemporaryFileDownloadPath:@"/Users/ben/Desktop/my_work_in_progress.txt.download"];
  [request setAllowResumeForFileDownloads:YES];
  [request startSynchronous];
 
  //The whole file should be here now.
  NSString *theContent = [NSString stringWithContentsOfFile:downloadPath];
}


這隻在下載數據到文件時有用,你必須設置allowResumeForFileDownloads爲yes,爲了:
任何你將來可能想恢復的下載(或者ASIHTTPRequest會在取消或釋放時刪除的臨時下載)
任何你想恢復的下載

而且,你必須設置一個臨時下載路徑(setTemporaryFileDownloadPath),用不完整數據的路徑,新數據將被追加到這個文件,當下載成功,這個文件會被移到downloadDestinationPath。

ASIHTTPRequest不會檢查accept-range頭(因爲額外head請求的負擔),所以僅在確定服務器能夠支持不完整下載時才使用此特徵。

補充內容 (2011-9-28 18:30):
stopped here tody...

 

直接從磁盤流式請求body

自v0.96開始,ASIHTTPRequest能夠使用磁盤文件作爲請求body。這意味着將請求body保持在內存不再必要,這樣一來,大型的post/put操作的將極大的減少內存使用量。

你可以以幾種方法來使用這一特徵:

ASIFormDataRequests

NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ignore"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"foo" forKey:@"post_var"];
[request setFile:@"/Users/ben/Desktop/bigfile.txt" forKey:@"file"];
[request startSynchronous];

ASIFormDataRequests在你使用setFile:forKey:,自動使用這一特性。請求將創建臨時文件,包含完整的請求body。文件一次寫入一些body的相關部分。請求被CFReadStreamCreateForStreamedHTTPRequest創建,使用此文件上的讀取流作爲來源。

常規的ASIHTTPRequest
如果你知道你的請求將會很大,可以在請求上打開磁盤流(streaming from disk):
[request setShouldStreamPostDataFromDisk:YES];

在下面的例子中,我們每次添加一個NSData對象。有兩個方法做這個事 - 從內存添加數據(appendPostData:),或者使用appendPostDataFromFile從文件添加內容。

NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ignore"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setShouldStreamPostDataFromDisk:YES];
[request appendPostData:myBigNSData];
[request appendPostDataFromFile:@"/Users/ben/Desktop/bigfile.txt"];
[request startSynchronous];


在本例中,我們想要直接put一個大文件。我們自己設置setPostBodyFilePath,ASIHTTPRequest將使用這個文件作爲post body。
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ignore"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setRequestMethod:@"PUT"];
[request setPostBodyFilePath:@"/Users/ben/Desktop/another-big-one.txt"];
[request setShouldStreamPostDataFromDisk:YES];
[request startSynchronous];

非常重要:你不應該在上面任何方法的同一個請求中使用setPostBody - 他們是互斥的,setPostBody只能用於你自己構建請求body,並計劃將請求body保留到內存的情型。

 

 

使用下載緩存

ASIDownloadCache 以及 ASICacheDelegate 的API在v1.8中已經改變,如果從v1.7升級的話,你需要更新你的代碼。
特別是,緩存策略的可用選項是不同的,並且你現在可以將多個緩存策略合併到單個請求中。

ASIHTTPRequest可以自動保存下載數據到一個緩存,以便以後使用。許多情況下這會很有幫助:
你想要訪問數據,在沒有因特網連接不能重新下載時;
你想下載些東西,僅在你上次下載後它有了變化時;
你用的內容永不改變,所以你只想下載它一次;

在之前的ASIHTTPRequest版本中,處理以上情況意味着你自己手動保存這些數據。使用一個下載緩存可以在一些情況下減少你自己編寫本地存儲機制的需求。

ASIDownloadCache是一個簡單的url緩存,可以被用於緩存get請求的響應。爲了符合響應緩存的條件,請求必須成功(沒有錯誤),服務器必須返回一個200 ok的http響應碼,或者從v1.8.1開始,支持301,302,303,307重定向的狀態碼。

開啓響應緩存很簡單:
[ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];

開啓之後,所有的請求會自動使用緩存。如果你喜歡,你可以爲獨立的請求設置共享緩存:
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];

緩存不限於單個,你可以創建任意多個緩存。當你自己創建緩存時,必須設置緩存的存儲路徑 -這應該是一個可寫的文件夾:

ASIDownloadCache *cache = [[[ASIDownloadCache alloc] init] autorelease];
[cache setStoragePath:@"/Users/ben/Documents/Cached-Downloads"];

// Don't forget - you are responsible for retaining your cache!
[self setMyCache:cache];

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[self myCache]];

 

關於緩存策略
緩存策略是在信息存儲於緩存中時你主要的控制方法,以及何時優先使用緩存的數據,而不是重新下載數據。

獨立請求的緩存策略可以使用它的cachePolicy屬性。緩存策略使用位掩碼(bitmask)定義,所以你可以組合多個選項來創建想要的策略:
// Always ask the server if there is new content available, 
// If the request fails, use data from the cache even if it should have expired.
[request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];

你可以使用下列選項來定義一個請求的緩存策略:

ASIUseDefaultCachePolicy
默認緩存策略,當你對請求應用此策略時,它會使用緩存的defaultCachePolicy,ASIDownloadCache的默認緩存策略是ASIAskServerIfModifiedWhenStaleCachePolicy,你不應該將此策略和其他策略組合使用。

ASIDoNotReadFromCacheCachePolicy
請求不會從緩存讀取數據

ASIDoNotWriteToCacheCachePolicy
請求不會保存到緩存

ASIAskServerIfModifiedWhenStaleCachePolicy
這是ASIDownloadCaches的默認緩存策略。使用了它,請求會首先查看緩存中是否有可用的緩存響應數據。如果沒有,請求會照常進行。
如果有沒有過期的緩存數據,請求會使用它而不去訪問服務器。如果緩存數據過期了,請求將執行一個有條件的get去獲取是否有可用的升級版本。如果服務器表示緩存的數據就是最新的,那麼緩存的數據將被使用,新的數據不會被下載。這時,緩存的過期時間(expiry date)將根據服務器新的過期時間而被更新。如果服務器提供了更新內容,則將被下載,新的數據和過期時間將被寫入緩存。

ASIAskServerIfModifiedCachePolicy
這個策略和ASIAskServerIfModifiedWhenStaleCachePolicy相同,只是請求每次都會詢問服務器是否有新的數據

ASIOnlyLoadIfNotCachedCachePolicy
只要存在緩存數據,總是會被使用,即使它已經過期。

ASIDontLoadCachePolicy
請求僅在響應已經被緩存時成功。如果請求沒有任何響應被緩存,請求會終止,並且也不會爲此請求設置錯誤。

ASIFallbackToCacheIfLoadFailsCachePolicy
如果請求失敗,將會退回到緩存數據。如果失敗後使用了緩存的數據,請求將會成功而不報錯。你一般會將它和其他策略混合使用,因爲此策略僅在發生問題時用於指定行爲。


當你爲緩存設置了defaultCachePolicy屬性,所有的請求將會使用這個緩存策略,除非他們自己設置了自定義的策略。

 

 

關於存儲策略
存儲策略允許你定義特定響應的緩存將被保存多久,ASIHTTPRequest目前支持兩種存儲策略:
ASICacheForSessionDurationCacheStoragePolicy 是默認值。響應僅在會話期間被保存,並將在緩存首次使用後被刪除,或者當調用[ASIHTTPRequest clearSession] 時被刪除。
使用ASICachePermanentlyCacheStoragePolicy,緩存數據將被永久保存,要用這個存儲策略,可將它設置到一個請求上:
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];

要手動清除緩存,調用clearCachedResponsesForStoragePolicy,傳入你希望清除的緩存數據的存儲類型:
[[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy];

 

 

其他緩存特性
// 當你關掉shouldRespectCacheControlHeaders, 響應數據將被保存,即使服務器明確要求不要緩存他們
// (eg with a cache-control or pragma: no-cache header)
[[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO];

爲請求設置secondsToCache,覆蓋了由服務器設置的任何過期時間,一直保存響應數據直到secondsToCache秒到期。
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setSecondsToCache:60*60*24*30]; // Cache for 30 days


在請求運行後,didUseCachedResponse將在響應由緩存返回時,返回yes
[request didUseCachedResponse];


詢問緩存請求數據的保存路徑,這是使用下載緩存最高效的方式,因爲數據不必在請求完成後被拷貝到緩存
[request setDownloadDestinationPath:
   [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]];


寫你自己的緩存

如果你已經有一個下載緩存,並想將它接入到ASIHTTPRequest,或者你想要寫自己的緩存,可以讓你的緩存實現ASICacheDelegate協議。

 

 

 
調試選項

ASIHTTPRequest提供了一些有助於調試請求行爲的標誌。這些標誌可以在ASITHHPRequestConfig.h中找到。
當你打開這些標誌後,請求會把他們乾的事打印到控制檯。

DEBUG_REQUEST_STATUS
打印總體請求的生命週期信息 -- 開始,結束上傳,結束下載 等等。

DEBUG_THROTTLING
打印(粗略的)有多少帶寬被使用的信息,如果請求被限流,還包括這如何發生的信息。同DEBUG_REQUEST_STATUS聯合使用,可能對調試超時有幫助,因爲你可以看到在哪一點請求停止了收發數據。

DEBUG_PERSISTENT_CONNECTIONS
打印請求如何重用持續連接的信息,如果你看到輸出下面的信息:
Request attempted to use connection #1, but it has been closed - will retry with a new connection

…這表示你設置到persistentConnectionTimeoutSeconds的值可能過高,參看“配置持續連接”小節獲得更多信息。

DEBUG_HTTP_AUTHENTICATION
從v1.8.1後添加,這會打印出關於請求如何處理http驗證(基本/摘要/ntml)的信息。

DEBUG_FORM_DATA_REQUEST
打印ASIFormDataRequest將發送的請求body的概要。僅在使用ASIFormDataRequest有用。

(全文完) 
====================================================================
© Ben Copsey, All-Seeing Interactive 2008-2011.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章