[ios 站在巨人的肩膀上開發 之]ASIHttpRequest 使用指南(一)

英文原文:http://allseeing-i.com/ASIHTTPRequest/How-to-use

翻譯:http://blog.csdn.net/workhardupc100/article/details/6941685

最近在做web server和client,所以自然就用到了ASIHttpRequest這個必須寵愛的庫。轉一個備忘一下。

請結合 :[ios 站在巨人的肩膀上開發 之]ASIHttpRequest 使用指南(二) 學習ASIHTTPRequest的使用。


1.創建並運行一個請求

1.1 創建一個同步請求

創建一個同步請求是使用ASIHTTPRequest的最簡單的方式。發送消息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的同步請求時,你的應用的用戶界面會被鎖定並且在整個網絡請求期間不可用。

1.2 創建一個異步請求

做與上一個例子相同的事情,但是這個請求在後臺運行。

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

       }

我們設置了請求的delegate屬性,所以當請求完成或失敗時,我們能夠接到通知。

這是創建異步請求的最簡單的方式,它會在一個位於後臺的全局的NSOperationQueue中運行。爲了實現更多複雜的操作(例如跟蹤多個請求的處理過程),你可能想創建自己的queue,這是下面將要講到的。

1.3 使用blocks

從1.8版本起,我們可以在支持它們的平臺上通過使用blocks完成上面例子的功能。

       - (IBAction)grabURLInBackground:(id)sender

      {

              NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];

              __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

              [request setCompletionBlock:^{

                    // Use when fetching textdata

                    NSString *responseString = [request responseString];

                    // Use when fetching binarydata

                    NSData *responseData = [request responseData];

              }];

              [request setFailedBlock:^{

                    NSError *error = [request error];

              }];

              [request startAsynchronous];

       }

    注意當我們聲明這個請求時標識符__block的使用,這是非常重要的!它告訴這個block不要retain這個請求,因爲這個請求總是retain這個block,這樣就避免了一個retain-cycle 。

1.4 使用隊列

這個例子做相同的事情,但是這次我們爲我們的請求創建一個NSOperationQueue。

使用你創建的NSOperationQueue(或者下面講到的ASINetworkQueue),就可以對異步請求進行更多的控制。當使用一個queue的時候,只有一定數量的請求同時運行。如果你向一個隊列中添加的請求多於屬性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 anNSOperationQueue

       }

       - (void)requestDone:(ASIHTTPRequest *)request

       {

              NSString *response = [request responseString];

        }

        - (void)requestWentWrong:(ASIHTTPRequest *)request

       {

              NSError *error = [request error];

       }

       在上面的例子中,’queue’是我們控制器的一個retainedNSOperationQueue屬性。

       我們設置自定義的selectors,它們會在請求成功或失敗後被調用。如果你不設置這些,默認的(requestFinished:和requestFailed:)會向前面的例子中那樣被使用。

 

1.5 在代理方法中處理多個請求的成功和失敗狀態

如果你需要處理多個請求的成功和失敗的狀態,你可以有幾種選擇:

1.           如果你的請求是相同的類型,但是你想區分它們,你可以用自定義的數據設置每個請求的字典類型的屬性userInfo,這樣你就可以在請求的成功或失敗的代理方法中讀取設置的值,通過這些值區分不同的請求。更簡單的一種方式,你可以設置請求的tag屬性。這兩種屬性是提供給你使用的,它們不會被髮送到服務端。

2.           對於每個請求,如果你需要一個完全不同的方式處理成功或失敗的返回結果,你可以針對每個請求設置不同的setDidFinishSelector和setDidFailSelector。

3.           對於更復雜的情況,或者說如果你想在後臺解析返回結果,可以這堆每種類型的請求,創建ASIHttpRequest的一個最小子類,並且重載方法requestFinished:和failWithError:。

注意:最好避免在代理方法中通過請求的URL區分不同的請求,因爲當重定向的時候URL屬性會改變。如果你真的想使用請求的URL,可以使用[request originalURL],這樣總會是第一次請求的url。

1.6 關於ASINetworkQueues

    ASINetworkQueue是類NSOperationQueue的子類,它提供了一些額外的功能。

它主要的目的是使你能夠跟蹤整個queue的上傳和下載的過程。

另外,ASINetworkQueues提供了一些額外的代理方法:

l   requestDidStartSelector

    隊列中的每個請求開始執行的時候調用這個方法。你可以使用它作爲”設置你添加到queue的請求的代理方法並指定這個請求的didStartSelector”的替代方法。

l   requestDidReceiveResponseHeaderSeletor

當隊列中的一個請求收到服務端回覆的請求頭時被調用。對於大數據量的下載,這個會在真正的下載完成之前被調用。你可以使用這個作爲“設置你添加到queue中的請求的代理並且指定請求的didFailSelector”的一個替代方案。

l   requestDidFinishSelector

    當隊列中一個請求成功時被調用。你可以使用這個作爲“設置你添加到隊列中請求的代理並指定一個didFinishSelector”的一個替代方案。

l    requestDidFailSelector

    當隊列中一個請求失敗時被調用。你可以使用這個作爲“設置你添加到隊列中請求的代理並指定一個didFailSelector”的一個替代方案。

l     queueDidFinishSelector

當整個隊列完成時才被調用,不管單個的請求是成功還是失敗。

要使用這些,可以設置隊列的代理(而不是設置請求的代理)爲實現這些selector的控制器。

       ASINetworkQueues的使用和NSOperationQueues有一點不同,就是添加到ASINetworkQueues的請求並不會馬上開始執行。當使用一個ASINetworkQueue時,添加你想運行的操作,然後調用[queue go]。當你開啓一個隊列並打開“進度控制”標誌時,它首先會爲隊列中的每個請求發送一個獲取頭部信息的請求,以獲得要下載的數據的大小。一旦隊列獲得了要下載的大小,它能準確的顯示整個下載過程,真正的下載也會開始。

注意:

       當你添加一個請求到一個已經開始的ASINetworkQueue中會發生什麼?當你使用ASINetworkQueue跟蹤一些請求的整個進展,當添加一個新的請求到隊列,只有新的請求開始運行的時候,整個隊列會進入後臺運行。當隊列開始運行的時候後,新添加的請求不會再請求頭部信息,所以當你一次添加很多請求到一個已經開始運行的隊列,整個進度不會立刻更新。

如果一個隊列已經開始運行,你不需要再調用[queuego]。

 

    當ASINetworkQueue中的一個請求執行失敗時,隊列默認會取消所有的請求。你可以通過 [queue setShouldCancelAllRequestsOnFailure:NO]

取消這個屬性。

 ASINetworkQueue只能運行ASIHTTPRequest操作,它們不能用於一般的操作。添加一個非ASIHTTPRequest的NSOperation會觸發一個異常。

     注意:這個一個完整的演示基本的創建和使用ASINetworkQueue例子:http://gist.github.com/150447

1.7 取消一個異步請求

       調用[request cancel]取消一個異步請求(一個以[ [request startAsynchronous] ]開始的請求或者你創建的運行在隊列中的請求)。注意你不能取消一個同步請求。

當你取消一個請求,這個請求會把這個當作一個錯誤,會調用請求的代理或者隊列的代理的失敗處理方法。如果你不希望這個行爲,在取消一個請求之前設置這個請求的代理爲nil或者使用方法clearDelegatesAndCancel取消請求。

           //Cancels an asynchronous request

           [requestcancel]

           //Cancels an asynchronous request, clearing all delegates and blocks first

            [requestclearDelegatesAndCancel];

使用一個ASINetworkQueue時,當你取消了隊列中的一個請求,那麼隊列中所有的請求都會被取消除非你設置了隊列的屬性shouldCancelAllRequestsOnFailure爲NO(YES是默認值)。

        // When a request inthis queue fails or is cancelled, other requests will continue to run

        [queuesetShouldCancelAllRequestsOnFailure:NO];

        // Cancel all requestsin a queue

        [queue cancelAllOperations];

1.8 當一個請求將要結束之前安全的處理即將被銷燬的代理

請求不retain它們的代理,所以當請求正在執行的時候你的代理可能會被釋放掉,清除請求的代理屬性是至關重要的。在大多數情況下,如果你的代理將要被釋放,你也不再關心這個請求的狀態,所以你可能也想取消這個請求。

    在下面的例子中,我們的控制器有一個屬性爲retain的ASIHTTPRequest類型的實例變量。我們在方法delloc的實現中調用請求的clearDelegatesAndCancel方法,正好在在釋放這個請求之前。

       // Ddealloc method for ourcontroller

       - (void)dealloc

       {

              [requestclearDelegatesAndCancel];

               [requestrelease];

               ...

              [superdealloc];

       }

2.發送數據

2.1 設置請求頭

              ASIHTTPRequest *request =[ASIHTTPRequest requestWithURL:url];

              [requestaddRequestHeader:@"Referer"value:@"http://allseeing-i.com/"];

2.2 使用ASIFormDataRequest發送一個POST請求

    可以使用ASIFormDataRequest發送與網頁格式兼容的POST請求。以application/x-www-form-urlencoded格式POST數據或者以multipart/form-data格式POST二進制數據或文件。如果需要文件中的數據會從硬盤讀取,所以可以提交大的文件,只要你的Web服務器支持處理它們。

              ASIFormDataRequest *request = [ASIFormDataRequestrequestWithURL:url];

              [request setPostValue:@"Ben"forKey:@"first_name"];

              [request setPostValue:@"Copsey"forKey:@"last_name"];

              [request setFile:@"/Users/ben/Desktop/ben.jpg"forKey:@"photo"];

       ASIFormDataRequest會自動檢測通過方法setFile:forKey添加到POST的文件的mine類型,並且把這個添加到mime頭中發送到服務端。如果你願意,你可以使用更長的形式代替這種方式:

              ASIFormDataRequest *request = [ASIFormDataRequestrequestWithURL:url];

              // Upload a file on disk

              [request setFile:@"/Users/ben/Desktop/ben.jpg" withFileName:@"myphoto.jpg"andContentType:@"image/jpeg" forKey:@"photo"];

              // Upload an NSData instance

              [request setData:imageData withFileName:@"myphoto.jpg"andContentType:@"image/jpeg" forKey:@"photo"];

       你可以使用add接口針對一個參數發送多個值:

              ASIFormDataRequest *request = [ASIFormDataRequestrequestWithURL:url];

              [request addPostValue:@"Ben" forKey:@"names"];

              [request addPostValue:@"George"forKey:@"names"];

              [request addFile:@"/Users/ben/Desktop/ben.jpg"forKey:@"photos"];

              [requestaddData:imageData withFileName:@"george.jpg"andContentType:@"image/jpeg" forKey:@"photos"];

       查看ASIFormDataRequest.h獲取向POST添加參數的全部的方法列表。

2.3 PUT請求和自定義POSTs

    如果你想通過PUT發送數據,或者想通過POST但是想自己創建POST包體,可以使用appendPostData:or appendPostDataFromFile:。

              ASIHTTPRequest *request =[ASIHTTPRequest requestWithURL:url];

              [request appendPostData:[@"Thisis my data" dataUsingEncoding:NSUTF8StringEncoding]];

              // Default becomes POST when you useappendPostData: / appendPostDataFromFile: / setPostBody:

              [requestsetRequestMethod:@"PUT"];

       如果你想發送大量的數據但是不想使用ASIFormDataRequest,可以看下面將要介紹的“informationon streaming post bodies from disk ”。

3.下載數據

3.1 直接把回覆內容下載到文件

如果你請求的資源非常的大,你可以直接下載到文件以節省內存。這樣的話,ASIHTTPRequest不需要一下子在內存中維護所有的回覆內容。

              ASIHTTPRequest*request = [ASIHTTPRequest requestWithURL:url];

              [requestsetDownloadDestinationPath:@"/Users/ben/Desktop/my_file.txt"];

       當你使用屬性downloadDestinationPath下載數據到一個文件時,在請求處理過程中數據會保存在一個臨時文件中。當請求完成時,下面兩個事情的其中一個會發生:

l   如果數據是gzip壓縮的(查看gzip壓縮數據),被壓縮的文件會解壓到downloadDestinationPath指定的路徑下,臨時文件會被刪除。

l   如果數據不是壓縮的,臨時文件會被移動到downloadDestinationPath指定的路徑下,並會覆蓋同名的文件。

注意:如果回覆的包體是空的,文件不會被創建。如果一個請求可能會返回一個空的包體,再你做任何嘗試之前,請先檢查下文件是否存在。

3.2 處理到來的回覆數據

       如果你需要處理到來的包體數據(比如:當數據還在下載的時候使用“流解析“解析回覆的數據),實現代理的request:didReceiveData:方法(查看ASIHTTPRequestDelegate.h)。注意當你這樣做的時候,ASIHTTPRequest不會填充responseData,也不會把回覆數據寫入到downloadDestinationPath指定的路徑,如果你需要你必須自己保存回覆的數據。

3.3 讀取HTTP狀態碼

對於大部分HTTP狀態碼,ASIHTTPRequest不會做任何特殊的事情(除了重定向和認證狀態碼,查看下面的內容獲取更多信息),因此需要你查找問題並確認你是否操作得當。

              ASIHTTPRequest *request =[ASIHTTPRequest requestWithURL:url];

              [request startSynchronous];

              int statusCode = [requestresponseStatusCode];

              NSString *statusMessage = [request responseStatusMessage];

3.4 讀取回復的頭部信息

              ASIHTTPRequest*request = [ASIHTTPRequest requestWithURL:url];

              [requeststartSynchronous];

              NSString*poweredBy = [[request responseHeaders]objectForKey:@"X-Powered-By"];

              NSString *contentType = [[requestresponseHeaders] objectForKey:@"Content-Type"];

3.5 處理文本編碼

ASIHTTPRequest會嘗試從頭部的Content-Type字段讀取接收到的數據的文本編碼,如果它發現一個文本編碼,它會設置字段responseEncoding爲一個合適的 NSStringEncoding。如果它在頭部字段沒有發現一個文本編碼,它會使用字段defaultResponseEncoding的默認值(這個字段默認值爲NSISOLatin1StringEncoding)。

    當你調用[request responseString], ASIHTTPRequest會創建一個字符串,並嘗試以responseEncoding指定的編碼方式把接收到的數據轉化到創建的字符串中。

3.6 處理重定向

ASIHTTPRequest會自動的重定向到一個新的URL當它遇到下面的一個HTTP狀態碼,假設一個本地的頭已經發送:

l   301 Moved Permanently

l   302 Found

l   303 See Other

       當重定向發生時, responsedata的值(例如responseHeaders / responseCookies / responseData / responseString )會是從最後的地址接收到的值。

重定向的url的Cookies會存儲到一個全局的cookie存儲中,適當的時候會在一個重定向請求中表示服務器。

    你可以通過設置請求的屬性shouldRedirect爲NO關閉重定向。

注意:

       默認情況下,自動重定向只針對GET請求(沒有消息體)。指定301和302重定向需要使用最初的方法符合多數瀏覽器的工作方式,不是HTTP協議特定的。

爲了保存最初的方法(包括請求體)

針對301和302重定向,爲了保存最初的方法(包括請求體),需要在請求開始執行之前,設置請求的屬性shouldUseRFC2616RedirectBehaviour爲YES。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章